来自上一个问题:
Doing a static_assert that a template type is another template
Andy Prowl向我提供了这段代码,允许我static_assert
模板类型是另一种模板类型:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //success
};
int main(int,char**)
{
bar<foo<int>> b;
return 0;
}
这很有效。
但这不适用于foo<whatever>
的子类:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //fail
};
//Added: Subclass of foo<int>
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; //Changed: Using the subclass
return 0;
}
可以将Andy Prowl的is_instantiation_of
代码扩展为允许子类吗?
答案 0 :(得分:2)
正如KerrekSB在他的回答中所写,无法实现您发布的解决方案的同等一般性扩展。
但是,如果您可以放弃一些通用性,可以编写一个特定于foo
的类型特征(利用衍生到基础是少数几个转换之一的事实<在类型推导期间执行em> :
#include <type_traits>
template<typename T>
struct foo {};
template<typename T>
constexpr std::true_type test(foo<T> const&);
constexpr std::false_type test(...);
template<typename T>
struct is_instantiation_of_foo : public decltype(test(std::declval<T>())) { };
然后你会这样使用它:
template<typename FooType>
struct bar {
static_assert(is_instantiation_of_foo<FooType>::value, "");
};
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; // Will not fire
return 0;
}
这是live example。
答案 1 :(得分:2)
这在许多情况下似乎都有效:
#include <iostream>
#include <utility>
template <typename T> struct foo { };
struct foo_sub : foo<int> { };
// A fallback function that will only be used if there is no other choice
template< template <typename> class X >
std::false_type isX(...)
{
return std::false_type();
}
// Our function which recognizes any type that is an instantiation of X or
// something derived from it.
template< template <typename> class X, typename T >
std::true_type isX(const X<T> &)
{
return std::true_type();
}
// Now we can make a template whose value member's type is based
// the return type of isX(t), where t is an instance of type T.
// Use std::declval to get a dummy instance of T.
template <template <typename> class X,typename T>
struct is_instantiation_of {
static decltype(isX<X>(std::declval<T>())) value;
};
template <typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value,"");
};
int main(int,char**)
{
//bar<int> a; // fails the static_assert
bar<foo<int>> b; // works
bar<foo_sub> c; // works
return 0;
}
正如Yakk所指出的那样,如果你有一个来自foo
的多个实例的类,那么它不起作用的地方,例如
struct foo_sub2 : foo<int>, foo<double> { };
答案 2 :(得分:1)
你不能在C ++ 11中这样做。您基本上必须对所有类类型进行量化,并检查它们中是否有任何类型是候选者的基础。
TR2中有一个提案(我听说现在已经不存在了),可能会进入C ++ 14,添加特征std::bases
和std::direct_bases
,它们枚举给定类的基类,从而有效地解决您的问题(即将您现有的特征应用于每个基类)。
如果有帮助,GCC会在<tr2/type_traits>
中提供此特征。
答案 3 :(得分:1)
我在打电话,所以这可能不起作用。
目标是使用SFINAE
并重载以询问“是否存在与此编译时间特征问题相匹配的基类?”
template<template<typename>class Test>
struct helper {
static std::false_type test(...);
template<typename T, typename=typename std::enable_if< Test<T>::value >
static std::true_type test(T const&);
};
template<template<typename>class Test, typename T, typename=void>
struct exactly_one_base_matches :std::false_type {};
template<template<typename>class Test, typename T>
struct exactly_one_base_matches<Test,T,
typename std::enable_if<decltype(helper<Test>::test(std::declval<T>()))::value>::type>
:std::true_type {};
如果这对于通用测试不起作用,那么test
进行模式匹配的测试可能会发生。可能需要替换...
。我想不出办法来处理通过测试的多个基地......
我认为我们可以做得更好。调用上述内容可能会产生三种结果。
首先,一个父母或一个人匹配测试。其次,它与全能型相匹配。第三,它是模棱两可的,因为它可以通过多种方式通过测试。
如果我们改进了catch-all以捕获低优先级的所有内容(也许Ts...&&
),我们就无法编译成功条件。
从匹配一的SFINAE
,true
返回true,从catch-all match-none返回false。