Substitution failure is not an error

C++语言中当出现函数重载解析的时候会产生一个候选集,当集合中出现模板函数,在模板参数匹配失败时不认为这是一个编译错误而是取消这个候选项。

  1. class A {};
  2. class B {
  3. public:
  4. using type = int;
  5. };
  6. template <typename T>
  7. void f(T t, typename T::type = {}) {
  8. std::cout << __LINE__ << std::endl;
  9. }
  10. template <typename T>
  11. void f(T t) {
  12. std::cout << __LINE__ << std::endl;
  13. }
  14. int main() {
  15. A a;
  16. f(a);
  17. return 0;
  18. }

当第一个模板函数匹配失败后没有编译错误而是去掉候选。T::type 代表的是类类型的成员,而不是类的成员。类的静态成员也属于类类型本身的成员。 using type = int; 等价于 typedef。

  1. class A {
  2. static const int type = 1;
  3. };
  4. template <typename T>
  5. void f(T t, typename T::type t2 = {}) {
  6. std::cout << __LINE__ << std::endl;
  7. }
  8. int main() {
  9. A a;
  10. f(a); // error
  11. return 0;
  12. }

虽然 type 是 A 类类型本身的成员,但它不是一个类型,不可以用在 typename T::type t2 = {} 这里。

利用 SFINAE 来判断是否拷贝

利用 arr_t 是否成功来判断参数是引用版本还是拷贝版本

  1. template <typename T, typename arr_t = int [sizeof(T) <= 8 ]>
  2. void f(T x) {
  3. //
  4. }
  5. template <typename T, typename arr_t = int [sizeof(T) > 8 ]>
  6. void f(const T& x) {
  7. //
  8. }
  9. A a;
  10. f(a);

std::enable_if

使用enable_if<>禁用模板,当 enable_if 第一个模板参数为 false 时,导致模板匹配不成功,从候选集中去掉该模板函数。

  1. template <typename T, typename some_t = typename std::enable_if<sizeof(T) <= 8>::type>
  2. void f(T x) {
  3. //
  4. }
  1. template <bool _Pre,typename _Tp=void >
  2. struct enable_if{};
  3. template <typename _Tp>
  4. struct enable_if<true,_Tp> { // false 匹配失败
  5. typedef _Tp type;
  6. };

小结

所谓的SFINAE规则就是在编译时进行查找替换,对于重载的函数,如果能够找到合适的就会替换,如果第一个不合适并不会报错,而会使用下一个替换直到最后一个,如果都不满足要求,那么才会报错。出现二义性的话也会报错。