为什么这段代码无法编译?

Why does this code fail to compile?

我知道这是一个非常笼统的标题,但我有一些代码,但我觉得很奇怪,无法编译。

Here 是该问题的演示。如果将 scalar_tdouble 更改为 float,则代码可以正常编译。为什么float不能在这里提升为double?事实上,如果把常量改成doubles(1.0)或者ints(1),它们也是不能提升的。这不是应该工作的那种东西吗?

完整代码示例:

#include <valarray>
#include <numeric>
#include <iterator>
#include <iostream>

template<typename T>
T sigmoid(const T &in)
{
    return 1.f / (1.f + std::exp(-in));
}

template<typename T>
T logit(const T &in)
{
    return std::log(in / (1.f - in));
}

using scalar_t = double;

int main(int argc, char **argv)
{
    std::valarray<scalar_t> f = { 0.1f, 0.3f, 0.5f, 0.9f };

    scalar_t alpha = 0.5f;
    scalar_t beta = -1.f;

    auto lC = logit(f);    
    std::valarray<scalar_t> skC = alpha * lC + beta;
    auto sC = sigmoid(skC);

    std::copy(std::begin(sC), std::end(sC), std::ostream_iterator<scalar_t>(std::cout, " "));
    std::cout << std::endl;

    scalar_t num = 0.7f;
    auto lS = logit(num);
    auto sS = sigmoid(alpha * lS + beta);

    std::cout << sS << std::endl;

    return 0;
}

您使用的operator -定义为

template <class T> std::valarray<T> operator- (const T& val, const std::valarray<T>& rhs);

这意味着它期望 valvalarray 中的元素类型相同。由于您在模板参数推导发生时使用 float,因此它会看到 valfloat,但 rhs 的元素类型为 double。由于这些类型不匹配,因此推导失败并且您会收到编译器错误。请记住,在模板参数推导期间不会发生任何转换。

这引发了一场关于如何在这些类型不可知模板中使用常量的非常有趣的讨论。令人惊讶的是,似乎有一个答案。检查 sigmoid 函数,我们发现它还使用 float 常量和 valarray<double> 但没有出现编译器错误。这是因为,std::exp(-in) 行将 valarray<double> 转换为使用标准库优化计算的表达式模板,无论出于何种原因,它都不关心 floatdouble(例如,它们提供过载)。所以我想出的解决方案是向 logit 函数添加一个一元 + 运算符,除了将 valarray<double> 转换为可以使用 float 常量的表达式模板外,它什么都不做。

Here为更新代码示例

新的 logit 函数如下所示

template<typename T>
T logit(const T &in)
{
    return std::log(in / (1.f - (+in)));
}

注意一元 + 运算符 (+in)

另请注意,NathanOliver 接受的解决方案回答了问题