编译时间模板实例化检查

时间:2015-01-27 20:55:59

标签: c++ templates c++11 typetraits

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

假设我有

template <typename T> struct known_type { };

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

3 个答案:

答案 0 :(得分:16)

如果您利用特定表达式可能会或可能不会在需要constexpr的地方使用这一事实,并且您可以查询以查看每个候选人的状态,则可以执行此操作。特别是在我们的例子中,没有定义的constexpr s不能作为常量表达式传递,noexcept是常量表达式的保证。因此,noexcept(...)返回true表示存在正确定义的constexpr

基本上,这会将constexpr视为是/否切换,并在编译时引入状态。

请注意,这几乎是一个黑客攻击,您需要针对特定​​编译器的变通方法(请参阅前面的文章),并且这个基于friend的特定实现可能会被标准的未来版本视为不正确。

有了这个......

用户Filip Roséen在专门针对它的his article中提出了这一概念。

他的示例实现是引用的解释:

constexpr int flag (int);
  

constexpr函数可以处于两种状态之一;不管是   可用于常量表达式,或者它不是 - 如果它缺少一个   定义它自动落在后一类 - 没有   其他状态(除非我们考虑未定义的行为)。

     

通常,constexpr函数应该完全按照它们的处理方式处理   是;函数,但我们也可以将它们视为单独的句柄   “变量”具有类似于bool的类型,其中每个“变量”都可以   有两个值之一;可用或不可用。

     

在我们的程序中,如果你认为旗帜就是这样,它会有所帮助;一个手柄   (不是一个功能)。原因是我们永远不会真正调用旗帜   在评估的背景下,我们只对其当前状态感兴趣。

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

writer是一个类模板,在实例化时会创建一个   在其周围的命名空间中定义函数(具有   signature int flag(Tag),其中Tag是模板参数。)

     

如果我们再次将constexpr函数视为某些句柄的句柄   变量,我们可以将编写器的实例化视为一个   无条件写入可用于后面的变量的值   函数在friend-declaration。

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

如果你认为dependent_writer看起来像一个人,我不会感到惊讶   相当无意义的间接;为什么不直接实例化作家   我们想在哪里使用它,而不是通过dependent_writer?

     
      
  1. 编写器的实例化必须依赖某些东西来阻止立即实例化,并且;
  2.   
  3. dependent_writer用于可以将bool类型的值用作依赖项的上下文中。
  4.   
template<
  bool B = noexcept (flag (0)),
  int    =   sizeof (dependent_writer<B>)
>
constexpr int f () {
  return B;
}
  

以上可能看起来有点奇怪,但它真的很简单;

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

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

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

Live on Coliru.

答案 1 :(得分:2)

不可以,不能对未实例化的类进行编译时检查。但是,您可以建立实例化类的(静态)映射(在调试版本中),您可以在运行时检查它。

但是,通过比较预期实例化类的列表与实际实例化的类来分析链接二进制文件应该是可能的(但这是过去的编译时间和我的知识)。

答案 2 :(得分:-1)

没有办法做到这一点。所以我会说:不。