c++ 模板实例化适用于 int、long 等,但不适用于 float、double 等

c++ template instantiation works with int, long, etc. but not float, double, etc

我正在做一个小的 C++ 项目,为此我创建了一个很好用的 std::ostream 包装器,名为 Logger

在 class 中,我定义了一个模板化运算符:

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_integral<T> 替换 std::is_arithmetic<T> 并使用 std::is_floating_point<T> 添加第二个模板函数,但这并没有改变任何东西,积分模板被实例化,但是浮点模板不是,导致与上面相同的错误消息。

如果我为 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);

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

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);
}

T 是双精度时,这个东西 std::enable_if_t<std::is_arithmetic<T>::value, T> 也解析为 double

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

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

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

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

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

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<char>::valuetrue 所以你需要取消那个案例的资格。