我为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;
}
如果集合类可以从任何具有任意数量模板参数的类或类模板派生,则为点。 :)
答案 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
}
注意:我必须更改collection
的模板,以便它也包含模板。这使得生活变得非常有趣,但它确实使最终结果更具可读性并产生了一些有趣的元函数。