C++11 如何使SFINAE表达式与模板类和非模板类一起工作?

C++11 如何使SFINAE表达式与模板类和非模板类一起工作?,c++11,sfinae,C++11,Sfinae,我为SFINAE制作了以下内容: // Type 'type' exists iff X is a base of COLLECTION template<typename X, typename COLLECTION, typename RET_TYPE = void> struct enable_if_is_base_of : std::enable_if<std::is_base_of<X, COLLECTION>::value, RET_TYPE> {

我为SFINAE制作了以下内容:

// Type 'type' exists iff X is a base of COLLECTION
template<typename X, typename COLLECTION, typename RET_TYPE = void>
struct enable_if_is_base_of : std::enable_if<std::is_base_of<X, COLLECTION>::value, RET_TYPE>
{};

如果集合类可以从任何类或具有任意数量模板参数的类模板派生,则获得额外积分:

经过多次尝试和测试,我想出了一个解决方案:

#include<iostream>
#include<type_traits>
using namespace std;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Place Holder Class
template <typename ...T>
class PH {};

// Select a Collection to Test On

// Since a collection is a template class that can derive from a non-template class
// or template classes of an arbitrary number of typename parameters, these 
// functions selects one instance of the collection, for which the base can be
// tested for.
template <
    template <template <typename...> class, typename...> class C
    , template <typename...> class TT
    , typename...T>
C<TT,T...> collection_selector(TT<T...>);

template <template <template <typename...> class, typename...> class C, typename T>
C<PH,T> collection_selector(T);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C>
is_base_of<T, decltype(collection_selector<C>(declval<T&>()))> enable_if_is_base_of_truth_selector();

// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C>
using is_base_of_truth = decltype(enable_if_is_base_of_truth_selector<T, C>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C, typename RT>
typename enable_if<is_base_of<T, decltype(collection_selector<C>(declval<T&>()))>::value, RT>::type enable_if_is_base_of_selector();
//typename enable_if<is_base_of_truth<T, C>::value, RT>::type enable_if_is_base_of_selector();

// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C, typename RT = void>
using enable_if_is_base_of = decltype(enable_if_is_base_of_selector<T, C, RT>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Select type from list
template <unsigned int I, typename ...Ts>
struct items;

template <typename T, typename ...Ts>
struct items<0, T, Ts...>
{
    typedef T type;
};

template <unsigned int I, typename T, typename ...Ts>
struct items<I, T, Ts...> : items<I-1, Ts...>
{
};

template <unsigned int I>
struct items<I>
{
    typedef int type;
};

template <unsigned int I, typename ...Ts>
typename items<I, Ts...>::type get_item_selector();

template <unsigned int I, typename ...Ts>
using get_item = decltype(get_item_selector<I, Ts...>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Classes to test on
class A{};
template <typename T> class B{};
template <typename T0, typename T1> class BB{};
class C{};
template <typename T> class D{};
template <typename T0, typename T1> class DD{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Collection of valid types
template <
    template <typename...> class TT
    , typename...Ts>
class collection 
    : A                                        // class A
    , B<get_item<0,Ts...>>                     // class B<X>
    , BB<get_item<0,Ts...>, get_item<1,Ts...>> // class BB<Y, Z>
{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to enable on
template <typename T>
enable_if_is_base_of<T, collection> test(T&&)
{
}

int main()
{
    cout << "-------------------------------------------------" << endl;
    cout << "Should all be 1." << endl;
    cout << is_base_of_truth<A             , collection>::value << endl;
    cout << is_base_of_truth<B<char*>      , collection>::value << endl;
    cout << is_base_of_truth<BB<int, float>, collection>::value << endl;
    cout << "-------------------------------------------------" << endl;
    cout << "Should all be 0." << endl;
    cout << is_base_of_truth<C              , collection>::value << endl;
    cout << is_base_of_truth<D<char>        , collection>::value << endl;
    cout << is_base_of_truth<DD<int, float&>, collection>::value << endl;
    cout << "-------------------------------------------------" << endl;

    // These succeed to compile since C, D<X> and DD<Y, Z> are the base of collection
    test(A());
    test(B<int>());
    test(BB<int, long>());

    // These fail to compile since C, D<X> and DD<Y, Z> are NOT the base of collection
    //test(C());             // fails as expected
    //test(D<int>());        // fails as expected
    //test(DD<int, long>()); // fails as expected

}

注意:我必须更改集合的模板,以便它也包含一个模板。这使生活变得非常有趣,但它确实使最终结果更具可读性,并产生了一些有趣的元函数。

您无法检查类型X是否是某个未知Y的模板类集合的基类。这从根本上说是不可能的,因为集合可能是一种专业化,不像主模板定义是从X派生的。你可能想检查一下,如果某个类型X的类是B,那么B是集合的基础吗?你是说?你能为你想要的特征添加一个用例吗?嗯,不完全是这样。看到这个了吗。添加了用例。
                      class A {};
template <typename X> class B {};
                      class C {};
template <typename X> class D {};

template <typename X> class collection : A, B<X> {};

// Stuff where the magic happens
...

// Testing the magic
template <typename X> // or other template declaration
typename enable_if_is_base_of<X, collection>::type fn(X&& x) { }
// May require more than one fn() declaration.

int main() {

    fn(A());
    fn(B<int>());
    // fn(C()); // Fails to compile
    // fn(D<int>()); // Fails to compile
    return 0;
}
#include<iostream>
#include<type_traits>
using namespace std;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Place Holder Class
template <typename ...T>
class PH {};

// Select a Collection to Test On

// Since a collection is a template class that can derive from a non-template class
// or template classes of an arbitrary number of typename parameters, these 
// functions selects one instance of the collection, for which the base can be
// tested for.
template <
    template <template <typename...> class, typename...> class C
    , template <typename...> class TT
    , typename...T>
C<TT,T...> collection_selector(TT<T...>);

template <template <template <typename...> class, typename...> class C, typename T>
C<PH,T> collection_selector(T);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C>
is_base_of<T, decltype(collection_selector<C>(declval<T&>()))> enable_if_is_base_of_truth_selector();

// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C>
using is_base_of_truth = decltype(enable_if_is_base_of_truth_selector<T, C>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C, typename RT>
typename enable_if<is_base_of<T, decltype(collection_selector<C>(declval<T&>()))>::value, RT>::type enable_if_is_base_of_selector();
//typename enable_if<is_base_of_truth<T, C>::value, RT>::type enable_if_is_base_of_selector();

// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C, typename RT = void>
using enable_if_is_base_of = decltype(enable_if_is_base_of_selector<T, C, RT>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Select type from list
template <unsigned int I, typename ...Ts>
struct items;

template <typename T, typename ...Ts>
struct items<0, T, Ts...>
{
    typedef T type;
};

template <unsigned int I, typename T, typename ...Ts>
struct items<I, T, Ts...> : items<I-1, Ts...>
{
};

template <unsigned int I>
struct items<I>
{
    typedef int type;
};

template <unsigned int I, typename ...Ts>
typename items<I, Ts...>::type get_item_selector();

template <unsigned int I, typename ...Ts>
using get_item = decltype(get_item_selector<I, Ts...>());

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Classes to test on
class A{};
template <typename T> class B{};
template <typename T0, typename T1> class BB{};
class C{};
template <typename T> class D{};
template <typename T0, typename T1> class DD{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Collection of valid types
template <
    template <typename...> class TT
    , typename...Ts>
class collection 
    : A                                        // class A
    , B<get_item<0,Ts...>>                     // class B<X>
    , BB<get_item<0,Ts...>, get_item<1,Ts...>> // class BB<Y, Z>
{};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to enable on
template <typename T>
enable_if_is_base_of<T, collection> test(T&&)
{
}

int main()
{
    cout << "-------------------------------------------------" << endl;
    cout << "Should all be 1." << endl;
    cout << is_base_of_truth<A             , collection>::value << endl;
    cout << is_base_of_truth<B<char*>      , collection>::value << endl;
    cout << is_base_of_truth<BB<int, float>, collection>::value << endl;
    cout << "-------------------------------------------------" << endl;
    cout << "Should all be 0." << endl;
    cout << is_base_of_truth<C              , collection>::value << endl;
    cout << is_base_of_truth<D<char>        , collection>::value << endl;
    cout << is_base_of_truth<DD<int, float&>, collection>::value << endl;
    cout << "-------------------------------------------------" << endl;

    // These succeed to compile since C, D<X> and DD<Y, Z> are the base of collection
    test(A());
    test(B<int>());
    test(BB<int, long>());

    // These fail to compile since C, D<X> and DD<Y, Z> are NOT the base of collection
    //test(C());             // fails as expected
    //test(D<int>());        // fails as expected
    //test(DD<int, long>()); // fails as expected

}