繁体   English   中英

编译时间模板实例化检查

[英]Compile time template instantiation check

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

让我说我有

template <typename T> struct known_type { };

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

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

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

请注意,这几乎是一个黑客,您将需要特定编译器的变通方法(请参阅前面的文章),并且这个特定的基于friend的实现可能被认为是未来的标准修订版。

随着那个...

用户FilipRoséen专门针对它的文章中提出了这个概念。

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

constexpr int flag (int);

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

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

在我们的程序中,如果你认为旗帜就是这样,它会有所帮助; 句柄(不是函数)。 原因是我们永远不会在评估的上下文中实际调用flag,我们只对其当前状态感兴趣。

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

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

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

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

如果你认为dependent_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 Yes / No开关来指示是否已实例化类型。 因此,您需要为您拥有的每种类型提供单独的开关。

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。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM