【问题标题】:Enforcing invariants in metaprogramming在元编程中强制执行不变量
【发布时间】:2015-09-21 09:47:29
【问题描述】:

我希望能够检查元程序中使用的类的不变量。我的第一个天真的方法是

template <int N>
struct digit
{
  static_assert((N >= 0) && (N < 10), "bad invariant");
};

using boom = digit<99>;

然而,这编译没有任何问题。只有在构造非法类时才会触发静态断言。

添加额外的模板参数时可以:

#include <type_traits>

template <int N, 
          typename = typename std::enable_if<(N >= 0) && (N < 10)>::type>
struct digit;

using crash = digit<-7>;

当我想将此技术应用于用作类型列表的类时:

#include <type_traits>

template <typename ...> struct are_integral;

template <typename T, typename ...Ts>
struct are_integral<T, Ts...>
{
  static const bool value = std::is_integral<T>::value &&
                            are_integral<Ts...>::value;
};

template <>
struct are_integral<> : std::true_type { };



template <typename ...Ts,
          typename = typename std::enable_if<are_integral<Ts...>::value>::type>
struct list;

using ok = list<int, long, char>;
using bad = list<double>;

它根本不起作用,因为 gcc 抱怨说

错误:参数包“Ts”必须在模板的末尾 参数列表结构列表;

即使它可以工作,该类也是无用的,因为模板参数包不反映类型列表。

所以我尝试使用“非法”基类:

template <typename> struct check;

template <typename ...Ts>
struct list : check<typename std::enable_if<are_integral<Ts...>::value>::type>
{ };

using ok = list<int, long, char>;
using bad = list<double>;

这编译没有问题。

有没有办法在 c++11 中完成类似的事情,还是我必须等待概念?

【问题讨论】:

  • 如果你不介意间接,你可以使用别名模板而不是直接引用list 模板。不过,我不认为这是一个很好的解决方案。
  • 我刚才问的相关问题:stackoverflow.com/questions/11251569/…。如您所见,问题与模板实例化有关,编译器会尽可能长时间地延迟模板实例化。目前,我想不出一种在不显式访问类成员的情况下触发实例化的方法,但我会继续思考!

标签: c++ c++11 template-meta-programming


【解决方案1】:

出现你的问题是因为模板在别名的时候没有实例化,所以static_assert没有触发。

如果这是可以接受的,您可以添加一些间接并使用构建器元函数来创建您的编译时列表。该元函数将执行检查。

template <typename ...Ts>
struct make_list
{ 
    static_assert(are_integral<Ts...>::value, "all types must be integral");
    typedef list<Ts...> type;
};

using ok = make_list<int, long, char>::type;
using bad = make_list<double>::type;

另一种解决方案是使用虚拟类型将参数包包装成一等类型。

// dummy wrapper
template <typename ...>
struct pack;

template <typename ...> struct are_integral;

// specialization for wrapped packs
template <typename ...Ts>
struct are_integral<pack<Ts...>> : are_integral<Ts...>
{
};

template <typename T, typename ...Ts>
struct are_integral<T, Ts...>
{
  static const bool value = std::is_integral<T>::value &&
                            are_integral<Ts...>::value;
};

template <>
struct are_integral<> : std::true_type { };

// helper type which performs the check
template <typename Pack,
          typename = typename std::enable_if<are_integral<Pack>::value>::type>
struct list_helper;

// actual type (alias) you will expose
template <typename ...Ts>
using list = list_helper<pack<Ts...>>;

using ok = list<int, long, char>;
using bad = list<double>; // compiler error

在处理参数包时,使用包装器通常会派上用场,因为它使它们更易于操作:包装器与任何其他类型一样,can be stored,出现在参数列表中的任何位置,被传递给一元元函数等。

【讨论】:

  • 虽然这对于使用列表的人来说是可以的,但表达式 list&lt;Foo*, void, float[]&gt; 仍然可以是 usingd 或用作模板参数,编译器不会出现任何错误。我真的很希望能够验证在我自己的元编程代码中操作这些类的代码中没有不变量被破坏。
猜你喜欢
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
  • 2018-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多