如何使用SFINAE表达式来处理模板和非模板类?

时间:2014-12-31 15:20:29

标签: 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>
{};

适合这样的事情:

class A {}; class B {}; class C{};
class collection : A, B {};

template <typename X>
typename enable_if_is_base_of<X, collection>::type fn(X&& x) { }

int main() {

    fn(A());
    fn(B());
    // fn(C()); // Fails as expected
    return 0;
}

但是,如果我希望从模板和非模板类派生collection,我不确定如何进行此操作。

这样的事情:

                      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;
}

如果集合类可以从任何具有任意数量模板参数的类或类模板派生,则为点。 :)

1 个答案:

答案 0 :(得分: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

}

DEMO

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