【问题标题】:c++ template instantiation works with int, long, etc. but not float, double, etcc++ 模板实例化适用于 int、long 等,但不适用于 float、double 等
【发布时间】:2018-04-19 23:27:34
【问题描述】:

我正在开发一个小型 C++ 项目,为此我创建了一个漂亮且方便的 std::ostream 包装器,名为 Logger

在类中我定义了一个模板化操作符:

template<typename T, typename std::enable_if_t<std::is_arithmetic<T>::value, T> = 0>
friend Logger& operator<<(Logger& logger, const T& logMessage) {
    return logger << std::to_string(logMessage); // there is also an operator overload which takes a std::string& as argument.
}

当模板以整数类型实例化时,例如intlong,模板的作用就像一个魅力。 所以声明

someLoggerInstance << 123 << 456ULL << "\n";

编译得很好。

但是,如果我将语句更改为

someLoggerInstance << 12.3 << "\n";

编译器对我大喊模板无法实例化。

来自编译器的错误信息:

候选模板被忽略:替换失败[with T = double]:非类型模板参数不能有类型'typename std::enable_if_t::value, double>'(又名'double')

问题出在哪里?

我尝试将std::is_arithmetic&lt;T&gt; 替换为std::is_integral&lt;T&gt; 并使用std::is_floating_point&lt;T&gt; 添加第二个模板化函数,但这并没有改变任何东西,整数模板被实例化,但浮点模板不是,导致相同错误信息如上。

如果我为 double 类型创建额外的重载

friend Logger& operator<<(Logger& logger, const double& logMessage) {
    return logger << std::to_string(logMessage);
}

问题变得更糟,从那时起编译器试图在最后传递"\n",以下消息让我很恼火:

候选函数不可行:第二个参数没有从 'const unsigned char [1]' 到 'const double' 的已知转换

候选函数不可行:第二个参数没有从 'const unsigned char [1]' 到 'const std::string'(又名 'const basic_string, allocator >')的已知转换

任何帮助或建议都会很好! 谢谢!


编辑

感谢您的快速帮助。

现在像这样更改模板:

template<typename T, typename TEnable = std::enable_if_t<std::is_arithmetic<T>::value && !std::is_same<T, char>::value, T>>
friend Logger& operator<<(Logger& logger, const T& logMessage);

【问题讨论】:

    标签: c++ templates primitive-types


    【解决方案1】:

    要解决您的第一个问题,另一种 SFINAE 是使用 std::enable_if_t 来允许返回类型的替换失败...

    template <typename T>
    std::enable_if_t<std::is_arithmetic<T>::value, Logger&>
    friend operator<<(Logger& logger, const T& logMessage)
    {
        return logger << std::to_string(logMessage);
    }
    

    您的第二个问题是因为std::is_arithmetic&lt;char&gt;::valuetrue,所以您需要取消该案例的资格。

    【讨论】:

      【解决方案2】:

      T 是双精度时,这个东西std::enable_if_t&lt;std::is_arithmetic&lt;T&gt;::value, T&gt; 也解析为double

      但非类型模板参数被明确禁止为浮点类型。您的问题的正确最小示例是这样的:

      template<double d> // this is ill-formed
      void foo() {}
      

      基本原理是模板根据非类型参数的确切值进行专门化。对于直接相等比较存在问题的类型来说,这是一个问题。

      只需将std::enable_if_t 解析为有效类型,例如使用 0 初始化的指针或 int

      【讨论】:

        【解决方案3】:

        更新模板以使用类型模板参数:

        template
        <
            typename T
        ,   typename TEnabled = typename std::enable_if_t<std::is_arithmetic<T>::value, T>
        >
        friend Logger & operator <<(Logger & logger, const T & logMessage) {
            return logger << std::to_string(logMessage);
        }
        

        【讨论】:

        • 最好使用std::enable_if_t&lt;std::is_arithmetic&lt;T&gt;::value&gt;* = nullptr,允许disable_if。
        • @Jarod42 disable_ifstd::enable_if_t&lt;!std::is_arithmetic&lt;T&gt;::value&gt; 有什么区别?
        • @Jarod42:虽然它有效(并且一直有效),但目前禁止使用void* 类型作为非类型模板参数(尽管最终可能会被允许。参见EWG 175)。我推荐 int 喜欢 StoryTeller 的回答。
        • @songyuanyao:请参阅stackoverflow.com/a/41175742/2684539 了解我的意思(typename = std::enable_if_t&lt;cond&gt; 的形式可能有问题)。
        • @Jarod42 实际上可能需要也可能不需要,这取决于是否应该有更多的模板操作符
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-29
        • 1970-01-01
        • 1970-01-01
        • 2016-04-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多