如何使SFINAE表达式与模板和非模板类一起使用? [英] How to get a SFINAE expression to work with template and non-template classes?

查看:38
本文介绍了如何使SFINAE表达式与模板和非模板类一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为SFINAE做到了这一点:

I have made this for 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>
{};

对于以下情况,效果很好:

Which works well for something like this:

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

但是,如果我要收集 可以从模板以及非模板类派生而来,我不确定该怎么做。

However, if I want collection to be derived from a template as well as non-template classes, I'm not sure how I would go about that.

类似这样的事情:

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

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

Bonus point if the collection class can derive from any class or class template with any number of template parameters. :)

推荐答案

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

After much trying and testing I've come up with a solution:

#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

}

演示

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

NOTE: I had to change the template of the collection so that it included a template as well. This made life very interesting but it did make the final result more readable and made some interesting meta functions.

这篇关于如何使SFINAE表达式与模板和非模板类一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆