【问题标题】:Extending this template static_assert code to cover subclasses扩展此模板 static_assert 代码以涵盖子类
【发布时间】:2013-06-30 23:15:57
【问题描述】:

来自上一个问题:

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&lt;whatever&gt;子类

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

【问题讨论】:

  • @KerrekSB 这是一个完全不同的问题(但介绍相同)
  • 你不能把它做成一个独立的简短问题,比如“是否可以检查一个类是否派生自模板实例”?
  • 我的回答似乎也处理了模板别名问题。

标签: c++ templates c++11


【解决方案1】:

正如 KerrekSB 在他的回答中所写,无法实现您发布的解决方案的同样通用的扩展。

但是,如果您可以放弃一点通用性,您可以编写一个特定于 foo 的类型特征(利用派生到基类是 为数不多的转换之一这一事实 在类型推断期间执行):

#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

【讨论】:

  • 我正在考虑放弃通用性并根据具体情况重复此操作,似乎要简单得多。但是 Vaughn Cato 声称他的解决方案是通用的,并且解决了子类和别名问题。你怎么看?
  • 如果基类具有纯虚函数并且子类覆盖了它,您的解决方案将不起作用。这可以解决吗?现场示例:coliru.stacked-crooked.com/…
  • @roger.james:是的,只是让test 函数通过引用const 而不是通过值接受。我更新了答案。这是live example
【解决方案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> { };

【讨论】:

  • @Yakk:对不起,我不明白你的评论。您是在问bar&lt;std::string&gt; 是否有效?
  • (...) 不会接受某些类型,如果我没记错的话(但这可能只是一个警告)。而struct x:foo&lt;int&gt;,foo&lt;double&gt;{}; 类型会出现歧义问题。
  • @Yakk:使用 g++ 4.7.2 -W -Wall,我没有收到来自 isX(...) 使用 std::string 的警告。同意存在模棱两可的问题。
  • 5.2.2.7 -- 在 C++03 中它是未定义的,在 C++11 中它是有条件地用未定义的语义定义的(!)。将其替换为 vaiardic Ts const&amp;...,我认为您摆脱了那种可怕的措辞,同时仍然比其他重载更糟糕?或者传递一个指针并匹配它。
【解决方案3】:

你不能在 C++11 中做到这一点。您基本上必须量化 所有 类类型并检查它们中的任何一个是否是候选的基础。

TR2 中有一个提议(我听说现在已经不复存在),可能会在 C++14 中添加特征 std::basesstd::direct_bases 枚举给定类的基类,从而有效地解决您的问题(即将您现有的特征应用于每个基类)。

GCC 确实在 &lt;tr2/type_traits&gt; 中提供了这个特性,如果有帮助的话。

【讨论】:

    【解决方案4】:

    我正在使用手机,所以这可能不起作用。

    目标是使用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...&amp;&amp; 可能),我们可以将失败作为编译成功的条件。

    SFINAE 返回 true,从 match-one 返回 true,从 catch-all match-none 返回 false。

    【讨论】:

    • tested,该技术不起作用。 SFINAE 也没有尝试处理模棱两可的重载...
    猜你喜欢
    • 1970-01-01
    • 2012-04-17
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 2018-01-05
    • 2021-02-14
    相关资源
    最近更新 更多