c++ - 无效的模板实例和元程序编译正常?

标签 c++ templates c++11 template-meta-programming llvm-clang

我正在研究一个简单的解决方案,以解决常见的 “条件格式错误的类型” 问题(如 this 昨天的问题)。

在我的代码库中,我有一个模板来保存未实例化的模板并稍后实例化它们。像这样:

template<template<typename...> class F>
struct lazy
{};

namespace impl
{
    template<typename L , typename... ARGS>
    struct lazy_instance;

    template<template<typename...> class F , typename... ARGS>
    struct lazy_instance<lazy<F>,ARGS...> : public identity<F<ARGS...>>
    {};
}

template<typename L , typename... ARGS>
using lazy_instance = typename impl::lazy_instance<L,ARGS...>::type;

在哪里identity是身份元函数。
这可以按如下方式使用:

using vector = lazy<std::vector>;
using int_vector = lazy_instance<vector,int>;

所以我想到的解决方案是以这种方式包装条件的两个目标,并实例化条件的结果,因此只实例化选定的模板。为此,我修改了 lazyimpl::lazy_instance允许我们在 lazy 处传递模板参数实例化点:

template<template<typename...> class F , typename... ARGS>
struct lazy
{};

namespace impl
{
    template<typename L , typename... ARGS>
    struct lazy_instance;

    template<template<typename...> class F , typename... ARGS , typename... IARGS>
    struct lazy_instance<lazy<F,ARGS...>,IARGS...> : public identity<F<ARGS...>>
    {};
}

请注意,在这种情况下 lazy_instance也接受模板参数,但它们会被忽略。我已经通过这种方式为两种用例提供​​了相同的界面。

因此我们的主要问题,即对潜在病态类型的条件选择的评估可以按如下方式实现:

using ok = lazy_instance<typename std::conditional<true,
                                                   lazy<foo,int>,
                                                   lazy<foo,bool>
                                                  >::type
                        >;

在哪里foo是一个模板,其中 bool实例化是错误的,例如:

template<typename T>
struct foo;

template<>
struct foo<int>
{};

这似乎行得通,不是吗?伟大的。但几分钟后,我将 bool 标志更改为 false ,令人惊讶的是它也有效!即使foo<bool>特化未定义!

元程序也有一个 static_assert下面检查条件评估是否成功:

static_assert( std::is_same<ok,foo<int>>::value , "Mmmmm..." );

当我从 true 更改条件时至 false ,我更改了测试以查看那里发生了什么:

static_assert( std::is_same<ok,foo<bool>>::value , "Mmmmm..." );

元程序也通过了测试!即使显式实例化了 foo<bool> , 和 ok ,它应该是该实例的别名。
最后,我检查了没有 foo<bool> 没有匹配的特化”,直到我声明了一个 ok 类型的变量。 : ok anok;

我在 C++11 模式下使用 CLang 3.4 ( -std=c++11 )。我的问题是:这里发生了什么?这种行为是正确的还是 LLVM 错误?

编辑: Here是运行在ideone的SSCCE。它使用 GCC 4.8.1,但似乎具有相同的行为。

最佳答案

说来话长;简短。

你实际上没有实例化foo<bool>在以下表达式中:

std::is_same<ok, foo<bool>>::value;

什么时候发生隐式实例化

14.7.1 Implicit instantiation [templ.inst]

5) A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type might affect the semantics of the program.

7) If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.

上面的文字说的是类模板只有在需要它被完全定义的上下文中使用时才会被隐式实例化,例如当声明所述模板的对象时,或者当试图访问其中的东西时.

检查一种类型是否与另一种类型相同不算作此类上下文,因为我们只是比较两个名称


什么时候需要一个完全定义的对象?

该标准在多个不同位置引用了“完全定义”,主要是在它明确表示需要此类对象时。

关于何时需要完全定义对象的最简单定义是阅读以下内容,其中解释了它不是什么。

3.9p5 Types [basic.types]

A class that has been declared but not defined, or an array of unknown size or of incomplete element type, is an incompletely defined object type. Incompletely-defined object types and the void types are incomplete types (3.9.1). Objects shall not be defined to have an incomplete type.

上面的措辞表明,只要我们不将对象声明为不完整类型,我们就没有问题; IE。我们的模板不会被隐式实例化。

请参阅下面的示例,其中 (C) 和 (D) 尝试创建一个不完整类型的对象,两者 (A) 和 (B) 是合法的,因为它们不会导致隐式实例化

template<class T> struct A;

typedef A<int> A_int; // (A), legal
A<int> *     ptr;     // (B), legal
A<int>       foo;     // (C), ill-formed; trying to declare an object of incomplete-type
A<int>::type baz;     // (D), ill-formed; trying to reach into the definition of `A<int>`

关于c++ - 无效的模板实例和元程序编译正常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24106502/

相关文章:

c++ - 变量模板链接失败

azure - 为 key 保管库的 virtualNetworkRules 添加 ARM 模板

c++ - std::vector 的 Typedef 和 ostream 运算符

c++ - 为什么迭代 std::set 比迭代 std::vector 慢得多?

c++ - OpenCV c++ 断言在 cv::_InputArray::getMat 中失败 <i < 0>

c++ - 集合中的 lower_bound (C++)

c++ - 具有默认值的结构的大括号(聚合)初始化

windows - 免费的 Windows 64 位 C++11 编译器

c++ - C++ 中具有 int、函数、虚函数的 sizeof 类?

c++ - 从嵌套的 STL 模板推断类型