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.
}
当模板实例化为整数类型时,如 int
或 long
,模板就像一个魅力。
所以语句
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>::value
是 true
所以你需要取消那个案例的资格。
我正在做一个小的 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.
}
当模板实例化为整数类型时,如 int
或 long
,模板就像一个魅力。
所以语句
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>::value
是 true
所以你需要取消那个案例的资格。