为什么我不能将继承的错误用作SFINAE?

时间:2013-10-22 11:07:20

标签: c++ multiple-inheritance sfinae

我有这段代码,但它没有编译:

#include <iostream>
#include <stdexcept>
#include <cassert>


template< class > struct id{};
template< class U, class V> struct base: public id<U>, public id<V>
{
    static const bool value = true;
};

template< class U, class V>
struct is_different
{

  typedef char (&true_)[1];
  typedef char (&false_)[2];

  template< class T, class K, bool  = base<T,K>::value >
  struct checker;



  template< class T, class K>
  static true_  test( checker<T,K>* );

  template< class , class >
  static false_  test(...);


  static const bool value = sizeof( test<U,V>(0) ) == sizeof(true_);

};


int main (void) 
{
    bool b1 = is_different<int,float>::value;
    bool b2 = is_different<int,int>::value; // <--- error

    std::cout << std::boolalpha << b1 << '\n'
                                << b2  << '\n';
    return 0;       
}

错误:

main.cpp: In instantiation of ‘struct base<int, int>’:
main.cpp:25:17:   required by substitution of ‘template<class T, class K> static char (& is_different<U, V>::test(is_different<U, V>::checker<T, K>*))[1] [with T = T; K = K; U = int; V = int] [with T = int; K = int]’
main.cpp:31:41:   required from ‘const bool is_different<int, int>::value’
main.cpp:39:38:   required from here
main.cpp:7:36: error: duplicate base type ‘id<int>’ invalid
 template< class U, class V> struct base: public id<U>, public id<V>

为什么我不能将重复继承的失败用作SFINAE?

1 个答案:

答案 0 :(得分:2)

SFINAE仅适用于函数签名中的内容。您获得的编译错误发生在base<T, K>的实例化中,这是从函数签名中删除的两个步骤。

那就是说,你为什么这么做呢?

template <typename T, typename U>
struct is_same { static const bool value = false; };

template <typename T>
struct is_same<T, T> { static const bool value = true; };

template <typename T, typename U>
struct is_different { static const bool value = !is_same<T, U>::value; };

修改

所以,要在评论中回答你的问题,让我解释一下SFINAE的更多内容。该标准规定了17.8.2p8中的SFINAE:

  

如果替换导致无效类型或rexpression,则类型扣除失败。无效类型或rexpression是使用替换参数写入时将导致格式错误的类型。只有函数类型的直接上下文中的无效类型和表达式及其模板参数类型才会导致演绎失败。 [注意:对替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等。这种副作用不在“直接背景”中,并且可能导致程序形成不良。 - 结束记录]

这里存在问题的是“直接背景”。你在做两件事就是从直接上下文中删除你的错误。

  • 在评估checker的默认参数表达式期间发生错误。虽然此评估是由checker<T,K>的参数列表中test的出现触发的,该列表被实例化以用于重载解析,但它不是直接上下文。您可以通过将base的定义更改为template <typename T, typename U> struct base {};来尝试此操作。这使得base实例化中的错误消失,将其替换为默认参数表达式上下文中的错误(“没有名为value的成员”)。但SFINAE仍然不会触发;你仍然会遇到一个严重的错误。
  • 错误发生在base定义的实例化中。这是从重载解析中删除的又一步骤。出于同样的原因,将成员放入base类型为T::foobar的成员无法查明T是否包含类型foobarbase的定义}不再是SFINAE环境。通过从图片中删除checker,您可以看到仅此一项是错误的,例如:这样做:

template <typename U, typename V> struct base: public id<U>, public id<V> {
    typedef void type;
};
template <typename U, typename V> struct is_different {
  struct true_ { char c[1]; };  // I don't like sizeof(reference type),
  struct false_ { char c[2]; }; // makes me nervous.
  template <typename T, typename K>
  static true_ test(typename base<T, K>::type*);
  template <typename, typename>
  static false_ test(...);

  static const bool value = sizeof(test<U, V>(0)) == sizeof(true_);
};

现在base实例化不会隐藏在模板默认参数表达式后面,但是正好在签名中,但您仍然会收到错误。