模板参数推导失败,SFINAE

Template argument deduction failed, SFINAE

本文关键字:失败 SFINAE 参数      更新时间:2023-10-16

编译此代码时:

#include <type_traits>
template <typename T>
void do_stuff(std::enable_if_t<std::is_integral<T>::value, T> &t) {}
template <typename T>
void do_stuff(std::enable_if_t<std::is_class<T>::value, T> &t) {}
int main() {
    int i = 1;
    do_stuff(i);
    return 0;
}

GCC表示:

37325975.cpp: In function ‘int main()’:
37325975.cpp:11:15: error: no matching function for call to ‘do_stuff(int&)’
     do_stuff(i);
               ^
37325975.cpp:4:6: note: candidate: template<class T> void do_stuff(std::enable_if_t<std::is_integral<_Tp>::value, T>&)
 void do_stuff(std::enable_if_t<std::is_integral<T>::value, T> &t) {}
      ^
37325975.cpp:4:6: note:   template argument deduction/substitution failed:
37325975.cpp:11:15: note:   couldn't deduce template parameter ‘T’
     do_stuff(i);
               ^
37325975.cpp:7:6: note: candidate: template<class T> void do_stuff(std::enable_if_t<std::is_class<T>::value, T>&)
 void do_stuff(std::enable_if_t<std::is_class<T>::value, T> &t) {}
      ^
37325975.cpp:7:6: note:   template argument deduction/substitution failed:
37325975.cpp:11:15: note:   couldn't deduce template parameter ‘T’
     do_stuff(i);
           ^

我也试过msvc 2013。

为什么我会出现这些错误?

实时演示

正如编译器所说,该参数类型是不可推导的,因此您需要手动提供模板参数,如下所示:

do_stuff<int>(i);

一个更好的选择是将std::enable_if放在返回类型或模板参数列表中:

//Return type
template <typename T>
std::enable_if_t<std::is_integral<T>::value>
do_stuff(T &t) {}
template <typename T>
std::enable_if_t<std::is_class<T>::value> 
do_stuff(T &t) {}
//Parameter list
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void do_stuff(T &t) {}
template <typename T, std::enable_if_t<std::is_class<T>::value>* = nullptr > 
void do_stuff(T &t) {}

通过这种方式,仍然可以推导出模板参数:

do_stuff(i);

为什么我会出现这些错误?

因为嵌套名称说明符是非推导上下文,模板参数推导失败。

使用限定id指定的类型的嵌套名称说明符(作用域解析运算符::左侧的所有内容)。

// the identity template, often used to exclude specific arguments from deduction
template<typename T> struct identity { typedef T type; };
template<typename T> void bad(std::vector<T> x, T value = 1);
template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1);
std::vector<std::complex<double>> x;
bad(x, 1.2);  // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
              // P1/A1: deduced T = std::complex<double>
              // P2 = T, A2 = double
              // P2/A2: deduced T = double
              // error: deduction fails, T is ambiguous
good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
              // P1/A1: deduced T = std::complex<double>
              // P2 = identity<T>::type, A2 = double
              // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
              // OK: T = std::complex<double>

当编译器试图解析do_stuff(int&)时,它会看到编译器告诉您的两个候选者。但它不能"倒推"找到一个满足std::enable_if_t<std::is_integral<T>::value, T> == intT,也不能找到一个符合std::enable_if_t<std::is_class<T>::value, T> == intT

正如TartanLlama的回答中所提到的,避免这种情况的方法是使参数可推导(例如do_stuff(T&)),并使返回类型后续模板参数依赖于T