扩展此模板static_assert代码以涵盖子类

时间:2013-06-30 23:15:57

标签: c++ templates c++11

来自上一个问题:

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代码扩展为允许子类吗?

4 个答案:

答案 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::basesstd::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...&&),我们就无法编译成功条件。

从匹配一的SFINAEtrue返回true,从catch-all match-none返回false。