13

从上一个问题:

做一个静态断言模板类型是另一个模板

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

这可以解决吗?

4

2 回答 2

11

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

当 template-id 指代别名模板的特化时,它等价于通过将其模板参数替换为别名模板的 type-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

...

—<em>结束示例]

在您的具体情况下,问题是在尝试匹配部分特化时,编译器不会推断出您的类型是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;
}

这是一个活生生的例子

于 2013-06-30T18:00:16.127 回答
1

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

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对于这种方法,您需要有一个有效的实例化。

于 2013-06-30T18:10:06.283 回答