使用SFINAE检测相同的类继承

时间:2014-06-05 07:19:31

标签: c++ c++11 template-meta-programming sfinae decltype

我试图编写一个元函数来检查作为可变参数模板参数传递的所有类型是否都是不同的。似乎最高效的方法是从一组类继承并检测是否存在错误。

问题是编译在以下代码中失败,而我希望SFINAE能够正常工作。

编辑。问题不是"如何编写该元功能"但是"我如何捕获双重继承错误并在它发生时输出false_type"。 AFAIK,它只适用于SFINAE。


template <typename T>
struct dummy {};

// error: duplicate base type ‘dummy<int>’ invalid
template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};

template <typename T>
true_type test(fail<T, T> a = fail<T, T>());

false_type test(...);

int main() {
    cout << decltype(test<int>())::value << endl;
}

Live version here


编辑。以前我曾尝试在专业化失败的情况下执行此操作,但它在使用相同的编译错误时无效。

template <typename T>
struct dummy {};

template <typename T, typename U>
struct fail : dummy<T>, dummy<U>, true_type {};

template <typename T, typename U = void>
struct test : false_type {};

template <typename T>
struct test<T, typename enable_if<fail<T, T>::value, void>::type> : true_type {};

Live version here.

5 个答案:

答案 0 :(得分:6)

您无法使用SFINAE捕获重复继承,因为在14.8.2p8 [temp.deduct] 下,它不是列出失败的原因之一。同样地,这是因为错误发生在模板推导的“直接上下文”之外,因为它是struct fail实例化的错误。

然而,有一种非常类似的技术适用于您的情况,即检测从派生类到多个基类的模糊转换。很明显,模糊的基类不能直接从单个派生类继承,但它可以在线性链中继承它们:

C<> A<int>
|  /
C<int> A<char>
|     /
C<char, int> A<int>
|           /
C<int, char, int>

现在从C<int, char, int>A<int>的转换不明确,并且由于14.8.2p8下列出的模糊转换 ,我们可以使用SFINAE来检测它:

#include <type_traits>

template<class> struct A {};
template<class... Ts> struct C;
template<> struct C<> {};
template<class T, class... Ts> struct C<T, Ts...>: A<T>, C<Ts...> {};
template<class... Ts> void f(A<Ts>...);
template<class... Ts> std::false_type g(...);
template<class... Ts> decltype(f((A<Ts>(), C<Ts...>())...), std::true_type()) g(int);
template<class... Ts> using distinct = decltype(g<Ts...>(0));

static_assert(distinct<int, char, float>::value, "!!");
static_assert(!distinct<int, char, int>::value, "!!");

答案 1 :(得分:4)

错误

  

prog.cpp: In instantiation of ‘struct fail<int, int>’: prog.cpp:23:29: required from here prog.cpp:9:8: error: duplicate base type ‘dummy<int>’ invalid struct fail : dummy<T>, dummy<U> {};

如上述诊断中所述,您无法在 base-specifier-list 中多次从同一 base 明确继承,并且从T开始U可能属于同一类型.. BOOM


等待,保持;什么关于 SFINAE

SFINAE 仅在模板本身的直接上下文中进行检查,超出声明的错误适合触发 SFINAE

  

14.8.2p8 模板参数扣除 [temp.deduct]

     
    

如果替换导致无效的类型或表达式,则类型推导失败。如果使用替代的arguemts编写,则无效的类型或表达式将是格式错误的,需要诊断。

         

只有函数类型及其templat eparameter类型的直接上下文中的无效类型和表达式才会导致演绎失败。

  

不正确的继承不会在紧急情况下发生,而且应用程序也是不正确的。

明确回答你的问题:由于继承从不在某个函数的声明中发生,所以 SFINAE 无法捕获格式错误的继承本身。

当然,你可以要求编译器生成一个使用继承的类,通过在函数声明中实例化它,但实际的(格式错误的)继承不在中直接背景

答案 2 :(得分:1)

简单std::is_same<>怎么样?据我所知,它直接模拟了你所期望的行为。

所以尝试这样的事情:

template<typename T, typename U>
fail
{
    fail(const T &t, const U &u)
    {
         static_assert(std::is_same<T,U>::value,"T and U must have a distinct type");
    }
};

甚至更好,直接在代码中使用std::is_same<T,U>

编辑:这是一个灵感来自Jarod42的解决方案,但它只使用一个类(使其更清晰:这是如何编写可变参数类模板的答案检测是否所有给定的类型都是不同的,这似乎是原始问题的早期版本之一中的期望目标):

#include <type_traits>

template <typename ...Ts> struct are_all_different {};
template <> struct are_all_different<> {static const bool value=true;};
template <typename T> struct are_all_different<T> {static const bool value=true;};

template <typename T1, typename T2>
struct are_all_different<T1, T2>
{
    static const bool value = !std::is_same<T1, T2>::value;
};

template <typename T1, typename T2, typename ...Ts>
struct are_all_different<T1,T2,Ts...>
{
    static const bool value = are_all_different<T1, T2>::value
                           && are_all_different<T1, Ts...>::value
                           && are_all_different<T2, Ts...>::value;
};

static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");

最后,对于n可变参数模板参数,这应检查所有n*(n+1)/2组合的相等性。

答案 3 :(得分:1)

据我了解,你想要一个特征来检查所有类型是否都是不同的,
以下可能有所帮助:

#include <type_traits>

template <typename T, typename ...Ts> struct is_in;

template <typename T> struct is_in<T> : std::false_type {};
template <typename T1, typename T2, typename ... Ts>
struct is_in<T1, T2, Ts...> : std::conditional<std::is_same<T1, T2>::value, std::true_type, is_in<T1, Ts...>>::type {};


template <typename ... Ts> struct are_all_different;

template <> struct are_all_different<> : std::true_type {};
template <typename T> struct are_all_different<T> : std::true_type {};

template <typename T1, typename T2, typename ... Ts> struct are_all_different<T1, T2, Ts...> :
    std::conditional<is_in<T1, T2, Ts...>::value, std::false_type, are_all_different<T2, Ts...>>::type {};


static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");

答案 4 :(得分:0)

我真的不明白你想要实现的目标,但我可以帮助你解决错误

template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};

template <typename T>
struct fail<T, T> : dummy<T> {};

错误是因为当您使用failT实例化U时,您基本上从同一个类继承两次,这是非法的,因此您需要创建专门化照顾这个案子。