编译时间模板实例化检查

图乔

是否可以检查在编译时是否已实例化模板类型,以便我可以在enable_if专业化中使用此信息?

假设我有

template <typename T> struct known_type { };

如果在编译时实例化了known_type,我可以以某种方式定义其值为true的is_known_type吗?

用户932887

如果您利用特定表达式可能会或可能不会在constexprs中使用的事实,并且可以查询以查看所拥有的每个候选者的状态,则可以执行此操作。特别是在我们的情况下,constexpr没有定义的s不能作为常量表达式传递,这是常量表达式noexcept的保证。因此,noexcept(...)返回true信号表明存在正确定义的constexpr

本质上,这将constexprs视为Yes / No开关,并在编译时引入状态。

请注意,这几乎是一种技巧,您将需要针对特定​​编译器的变通办法(请参见前面的文章),并且此特定的friend基于实现的实施可能会因将来的标准修订而被认为是错误的。

这样就不会了...

用户FilipRoséen他的专门文章中提出了这个概念

他的示例实现带有引述的解释:

constexpr int flag (int);

constexpr函数可以处于以下两种状态之一:它可以在常量表达式中使用,也可以不使用-如果它缺少定义,它会自动属于后一种类别-没有其他状态(除非我们考虑未定义的行为)。

通常,constexpr函数应该被正确地对待。函数,但我们也可以将它们视为具有类似于bool类型的“变量”的单独句柄,其中每个“变量”可以具有两个值之一;可用或不可用。

在我们的程序中,如果您认为flag只是那样,它会有所帮助;句柄(不是功能)。原因是我们永远不会真正在评估的上下文中调用flag,我们只对它的当前状态感兴趣。

template<class Tag>
struct writer {
  friend constexpr int flag (Tag) {
    return 0;
  }
};

writer是一个类模板,在实例化时将在其周围的名称空间中创建一个函数定义(具有签名int标志(Tag),其中Tag是模板参数)。

如果我们再次将constexpr函数视为某些变量的句柄,则可以将writer的实例视为对朋友声明中该函数后面的变量可用的值的无条件写入。

template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };

如果您认为dependent_writer看起来像是一个毫无意义的间接调用,我不会感到惊讶。为什么不直接在我们要使用它的地方实例化writer,而不是通过dependent_writer?

  1. 作者的实例化必须依赖于某种因素来防止立即实例化,并且;
  2. dependent_writer用于可以将bool类型的值用作依赖项的上下文中。
template<
  bool B = noexcept (flag (0)),
  int    =   sizeof (dependent_writer<B>)
>
constexpr int f () {
  return B;
}

上面的内容可能看起来有些怪异,但实际上非常简单。

  1. 如果flag(0)是一个常量表达式,则将B设置为true,否则将B设置为false。
  2. 隐式实例化dependent_writer(sizeof需要一个完全定义的类型)。

该行为可以用以下伪代码表示:

IF [ `int flag (int)` has not yet been defined ]:
  SET `B` =   `false`
  INSTANTIATE `dependent_writer<false>`
  RETURN      `false`
ELSE:
  SET `B` =   `true`
  INSTANTIATE `dependent_writer<true>`
  RETURN      `true`

最后,概念证明:

int main () {
  constexpr int a = f ();
  constexpr int b = f ();
  static_assert (a != b, "fail");
}

我将其应用于您的特定问题。想法是使用“constexpr是/否”开关指示类型是否已实例化。因此,对于每种类型,您都需要一个单独的开关。

template<typename T>
struct inst_check_wrapper
{
    friend constexpr int inst_flag(inst_check_wrapper<T>);
};

inst_check_wrapper<T>本质上包装了您可以给它提供的任何类型的开关。这只是原始示例的通用版本。

template<typename T>
struct writer 
{
    friend constexpr int inst_flag(inst_check_wrapper<T>) 
    {
        return 0;
    }
};

开关拨动开关与原始示例中的相同。它附带了您使用的某种类型的开关的定义。为了便于检查,请添加辅助开关检查器:

template <typename T, bool B = noexcept(inst_flag(inst_check_wrapper<T>()))>
constexpr bool is_instantiated()
{
    return B;
}

最终,类型“注册”为已初始化。就我而言:

template <typename T>
struct MyStruct
{
    template <typename T1 = T, int = sizeof(writer<MyStruct<T1>>)>
    MyStruct()
    {}
};

要求该特定构造函数后,便会立即打开该开关。样品:

int main () 
{
    static_assert(!is_instantiated<MyStruct<int>>(), "failure");
    MyStruct<int> a;
    static_assert(is_instantiated<MyStruct<int>>(), "failure");
}

住在Coliru上。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章