在阅读Tick库中的一段代码时,发现一个问题:
template<typename T>
class TestClass {
public:
template<typename = std::enable_if_t<std::is_integral<T>::value, int>>
std::false_type Func()
{
return {};
}
template<typename = std::enable_if_t<!std::is_integral<T>::value, int>>
std::true_type Func()
{
return {};
}
};
TestClass<int> t{}; // error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
编译会出错,疑问是为什么这里不能通过SFINE来排除第二个函数?也就是SFINE为什么没有生效;
首先看下TestClass<int> 的时候发生了什么,先删除第二个函数,通过cppinsight查看得到:
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class TestClass<int>
{
public:
template<typename type_parameter_0_0 = std::enable_if_t<std::is_integral<int>::value, int>>
inline std::integral_constant<bool, false> Func();
};
#endif
类模板Func声明进行了实例化,std::is_integral<int>::value中T被int替换,没有了模板参数推导的过程,即std::enable_if_t的参数没有模板参数的依赖,而第二个函数声明实例化过程中:
template<typename = std::enable_if_t<false, int>>
inline std::integral_constant<bool, true> Func();
std::enable_if_t<false, int>没有对应的类型type,导致编译出错;
SFINE触发的失败为:函数或者类的模板参数替换导致的失败【2】,而std::enable_if_t中T为TestClass的模板参数,并不是Func依赖的模板参数,因此不是SFINE的fail;
This rule applies during overload resolution of function templates: When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier(since C++20) are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors.
修改代码为:
template<typename T>
class TestClass {
public:
template<bool privateBool = true, typename = std::enable_if_t<privateBool && std::is_integral<T>::value, int>>
std::false_type Func()
{
return {};
}
template<bool privateBool = true, typename = std::enable_if_t<privateBool && !std::is_integral<T>::value, int>>
std::true_type Func()
{
return {};
}
};
TestClass<int> t{};
t.Func();
此时t.Func()就能正确的选择Func而没有编译报错。
参考资料:
【1】模板实例化
【2】https://en.cppreference.com/w/cpp/language/sfinae