【问题标题】:Using a template alias instead of a template within a template在模板中使用模板别名而不是模板
【发布时间】:2013-06-30 16:46:53
【问题描述】:

来自上一个问题:

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; //success
  return 0;
}

这很好用。

但如果我将这样的代码更改为使用 foo 的别名,事情就会变糟:

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 {};

//Added: alias for foo
template<typename T>
using foo_alt = foo<T>;

template<typename FooType>
struct bar {
  //Changed: want to use foo_alt instead of foo here
  static_assert(is_instantiation_of<foo_alt,FooType>::value, ""); //fail
};

int main(int,char**) {
  //both of these fail:
  bar<foo<int>> b;
  bar<foo_alt<int>> b2;

  return 0;
}

这可以解决吗?

【问题讨论】:

  • 嗯,看起来 foo_alttypedef-name 而不是 template-name...但这只会影响您的 @987654327 @;你可以保持 main 不变。
  • 我认为您可以检查两种类型是否是同一模板的实例化,这也适用于别名模板。

标签: c++ templates c++11


【解决方案1】:

不,它无法解决(至少在不显着改变设计的情况下无法解决)。问题是没有推断出模板别名,如 C++11 标准第 14.5.7/2 段所述:

当一个template-id指代一个别名模板的特化时,它等价于关联类型 通过将其模板参数替换为别名的类型 ID 中的模板参数而获得 模板。 [ 注意:永远不会推断出别名模板名称。—结束说明 ]

该段还提供了一个例子:

[例子

template<class T> struct Alloc { / ... / };
template<class T> using Vec = vector<T, Alloc<T>>;
Vec<int> v; // same as vector<int, Alloc<int>> v;

...

template<template<class> class TT>
void f(TT<int>);
f(v); // error: Vec not deduced                          <=== Relevant

...

结束示例 ]

在您的具体情况下,问题是当尝试匹配部分特化时,编译器不会推断您的类型是foo_alt 的实例化(因为foo_alt 是别名模板的名称),并选择主模板。

如果你想使用别名模板,你将不得不放弃一些通用性并创建一个特定于foo 的类型特征:

#include <type_traits>

template<typename T>
struct foo {};

template<typename T>
struct is_instantiation_of_foo : std::false_type { };

template<typename...Ts>
struct is_instantiation_of_foo<foo<Ts...>> : std::true_type { };

然后你可以这样使用:

template<typename FooType>
struct bar {
  static_assert(is_instantiation_of_foo<FooType>::value, ""); //fail
};

现在,以下程序中的任何断言都不会触发:

template<typename T>
using foo_alt = foo<T>;

int main(int,char**) {
  // None of these fail:
  bar<foo<int>> b;
  bar<foo_alt<int>> b2;

  return 0;
}

这是live example

【讨论】:

  • 也许我们可以做得更好。 SFINAE-safe 模式匹配推导出 template M 使得传入的类型是 M&lt;Ts...&gt; 对于某些 Ts...。然后检查std::is_same&lt;X&lt;Ts...&gt;,M&lt;Ts...&gt;&gt;,如果X&lt;Ts...&gt; 格式不正确,SFINAE 也会受到保护,其中X&lt;&gt; 是传入匹配的template...
  • @Yakk:你对我来说太天才了 :) 我不明白“SFINAE-推断”是什么意思,恐怕我需要在几分钟前盯着解决方案反正我明白了;)
  • 见 dyn 的回答:我不太清楚,在这个键盘上写代码很难。我的意思是“以 SFINAE 安全的方式推断”,所以如果它不匹配它只会说 false。 Dyn 发现了我错过的缺陷:他对我来说太聪明了!
  • @Yakk 不,他不太聪明,但他最后有一个 P ;)
  • @Yakk 你说得对,需要一些警卫来检查X&lt;Ts...&gt; 是否格式错误。更新了我的答案。
【解决方案2】:

如果你有一个别名模板改变被引用类的模板参数(就像在你的例子中;它只是重命名被引用的模板),那么你可以使用类似的东西(它不是最优雅的方式)

template < template<class...> class TT0, template<class...> class TT1,
           class... Ts1 >
struct is_from_same_template_helper
{
    template < class T = TT0<Ts1...>,
               class = typename std::enable_if<
                   std::is_same<TT0<Ts1...>, TT1<Ts1...>>::value
               >::type
             >
    static std::true_type test(int);

    template < class T = int >
    static std::false_type test(...);
};


template<template<class...> class, class>
struct is_instantiation_of : public std::false_type { };

template<template<class...> class TT0, template<class...> class TT1,
         class... Ts1>
struct is_instantiation_of<TT0, TT1<Ts1...>>
    : public decltype( is_from_same_template_helper<TT0, TT1, Ts1...>
                       ::template test<>(0) )
{ };

感谢 Yakk 指出您需要 SFINAE 类型的检查才能让 static_assert 失败(之前的版本由于 static_assert 之前的编译器错误而失败)。

因为只有别名模板的特化等于引用模板的特化;别名模板本身不等于引用的模板。

如果别名模板确实改变了模板参数,上面的实现可能会产生漏报。


作为 xy 问题,您可以将实现更改为:

#include <type_traits>


template < template <typename...> class TT0, template <typename...> class TT1 >
struct is_same_template : public std::false_type { };

template < template <typename...> class TT >
struct is_same_template < TT, TT > : public std::true_type { };


template < typename T0, typename T1 >
struct is_from_same_template : public std::false_type { };

template < template <typename...> class TT0, template <typename...> class TT1,
           typename... Ts0, typename... Ts1 >
struct is_from_same_template < TT0<Ts0...>, TT1<Ts1...> >
    : public is_same_template<TT0, TT1> { };


template<typename T>
struct foo {};

//Added: alias for foo
template<typename T>
using foo_alt = foo<T>;

template<typename FooType>
struct bar {
  //Changed: want to use foo_alt instead of foo here
  static_assert(is_from_same_template<foo_alt<int>, FooType>::value, "");
};

int main(int,char**) {
  //both of these succeed:
  bar<foo<int>> b;
  bar<foo_alt<int>> b2;

  return 0;
}

当然,对于这种方法,您需要有一个有效的 foo_alt 实例化。

【讨论】:

  • 看不懂TT1模板参数is_from_same_template_helper模板的作用。
  • @Constructor 正如我在之前的评论中“宣布”的那样,代码中存在一个错误(根据从 hard 更改为 SFINAE 错误时的编辑历史记录引入)。现在清楚了吗?
  • 哦,抱歉,我没有注意到您的评论。现在很清楚了,谢谢。如我所见,您无法决定更喜欢哪个关键字(typenameclass)。 :-)
猜你喜欢
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多