SFINAE 在返回类型中工作但不作为模板参数

2021-12-12 00:00:00 templates c++ c++11 sfinae

我已经多次使用 SFINAE 习语,而且我习惯于将我的 std::enable_if<> 放在模板参数中而不是返回类型中.但是,我遇到了一些不起作用的小案例,我不确定为什么.首先,这是我的主要内容:

int main(){富(5);富(3.4);}

这是触发错误的 foo 实现:

template::value>::type>自动 foo(T)->空白{std::cout <<"我是一个整数!
";}模板<类型名 T,typename = typename std::enable_if::value>::type>自动 foo(T)->空白{std::cout <<"我是一个浮点数!
";}

这里是一段假设等效的代码,可以正常工作:

template自动 foo(T)->类型名称 std::enable_if<std::is_integral<T>::value>::type{std::cout <<"我是集成商!
";}模板自动 foo(T)->类型名称 std::enable_if<std::is_floating_point<T>::value>::type{std::cout <<"我是一个浮点数!
";}

我的问题是:为什么 foo 的第一个实现会触发那个错误而第二个没有触发它?

<块引用>

main.cpp:14:6: 错误:重新定义模板"void foo(T)'自动 foo(T)^main.cpp:6:6: 注意:'templatevoid foo(T)' 先前在此处声明自动 foo(T)^main.cpp:在函数int main()"中:main.cpp:23:12: 错误:没有匹配的函数来调用 'foo(double)'富(3.4);^main.cpp:6:6: 注意:候选:模板无效的 foo(T)自动 foo(T)^main.cpp:6:6: 注意:模板参数推导/替换失败:main.cpp:5:10: 错误:struct std::enable_if<false, void>"中没有名为type"的类型typename = typename std::enable_if::value>::type>^

编辑:

工作代码和错误代码.

解决方案

您应该查看 14.5.6.1 函数模板重载(C++11 标准),其中定义了函数模板等效性.简而言之,不考虑默认模板参数,因此在第一种情况下,您定义了两次相同的函数模板.在第二种情况下,您在返回类型中使用了引用模板参数的表达式(再次参见 14.5.6.1/4).由于这个表达式是签名的一部分,你会得到两个不同的函数模板声明,因此 SFINAE 有机会工作.

I already used the SFINAE idiom quite a few times and I got used to put my std::enable_if<> in template parameters rather than in return types. However, I came across some trivial case where it didn't work, and I'm not sure why. First of all, here is my main:

int main()
{
    foo(5);
    foo(3.4);
}

Here is an implementation of foo that triggers the error:

template<typename T,
         typename = typename std::enable_if<std::is_integral<T>::value>::type>
auto foo(T)
    -> void
{
    std::cout << "I'm an integer!
";
}

template<typename T,
         typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
auto foo(T)
    -> void
{
    std::cout << "I'm a floating point number!
";
}

And here is a supposedly equivalent piece of code that works fine:

template<typename T>
auto foo(T)
    -> typename std::enable_if<std::is_integral<T>::value>::type
{
    std::cout << "I'm an integrer!
";
}

template<typename T>
auto foo(T)
    -> typename std::enable_if<std::is_floating_point<T>::value>::type
{
    std::cout << "I'm a floating point number!
";
}

My question is: why does the first implementation of foo triggers that error while the second one does not trigger it?

main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)'
 auto foo(T)
      ^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here
 auto foo(T)
      ^
main.cpp: In function 'int main()':
main.cpp:23:12: error: no matching function for call to 'foo(double)'
     foo(3.4);
            ^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T)
 auto foo(T)
      ^
main.cpp:6:6: note:   template argument deduction/substitution failed:
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>'
          typename = typename std::enable_if<std::is_integral<T>::value>::type>
          ^

EDIT :

Working code and faulty code.

解决方案

You should take a look at 14.5.6.1 Function template overloading (C++11 standard) where function templates equivalency is defined. In short, default template arguments are not considered, so in the 1st case you have the same function template defined twice. In the 2nd case you have expression referring template parameters used in the return type (again see 14.5.6.1/4). Since this expression is part of signature you get two different function template declarations and thus SFINAE get a chance to work.

相关文章