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

时间:2016-11-02 18:21:40

标签: c++ floating-point valarray

我知道这是一个相当普遍的标题,但我有一些代码,它让我感到奇怪,它无法编译。

Here是该问题的演示。如果您将scalar_tdouble更改为float,则代码编译正常。为什么不能在这里升级加倍?事实上,如果您将常量更改为double s(1.0)或int s(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;
}

2 个答案:

答案 0 :(得分:4)

您使用的operator -定义为

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

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

答案 1 :(得分:1)

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

Here是更新代码示例

并且新的float函数看起来像这样

logit

请注意一元+运算符template<typename T> T logit(const T &in) { return std::log(in / (1.f - (+in))); }

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