【问题标题】:How to get a SFINAE expression to work with template and non-template classes?如何让 SFINAE 表达式与模板和非模板类一起使用?
【发布时间】:2014-12-31 15:20:28
【问题描述】:

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

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

【问题讨论】:

  • 您无法检查类型 X 是否是某个模板类 collection&lt;Y&gt; 的基类,其中 Y 未知。这从根本上说是不可能的,因为collection&lt;Y&gt; 可能是一种特殊化,它不同于主模板定义确实 派生自X。您是否想检查某个类型 X 的类是否为 B&lt;X&gt;,那么 B&lt;X&gt; 是否是 collection&lt;X&gt; 的基础?
  • 你的意思是something like in this question 吗?
  • 你可以为你想要的特征添加一个用例吗?
  • 嗯,不完全是。看到这个demo
  • @PiotrS。添加了用例。

标签: c++11 sfinae


【解决方案1】:

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

#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 的模板,以便它也包含一个模板。这让生活变得非常有趣,但它确实使最终结果更具可读性,并产生了一些有趣的元函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-10
    • 1970-01-01
    • 2022-01-22
    • 2019-07-19
    • 1970-01-01
    相关资源
    最近更新 更多