无效的模板即时和元编程编译好吗?

Man*_*726 2 c++ templates llvm-clang template-meta-programming c++11

我工作的一个简单的解决方案,以共同的"上形成不良的类型条件"的问题(像这个昨天的问题).

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

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;
Run Code Online (Sandbox Code Playgroud)

identity身份元功能在哪里.
这可以使用如下:

using vector = lazy<std::vector>;
using int_vector = lazy_instance<vector,int>;
Run Code Online (Sandbox Code Playgroud)

因此,我想到的解决方案是以这种方式包装条件的两个目标,并实例化条件的结果,因此只有选定的模板才会被实例化.为此,我修改了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...>>
    {};
}
Run Code Online (Sandbox Code Playgroud)

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

因此,我们的主要问题是,对潜在不良类型的条件选择的评估可以实现如下:

using ok = lazy_instance<typename std::conditional<true,
                                                   lazy<foo,int>,
                                                   lazy<foo,bool>
                                                  >::type
                        >;
Run Code Online (Sandbox Code Playgroud)

在哪里foobool瞬间形成不良的模板,例如:

template<typename T>
struct foo;

template<>
struct foo<int>
{};
Run Code Online (Sandbox Code Playgroud)

它似乎工作,不是吗?大.但几分钟后我将布尔标志改为false,令人惊讶的是它也有效!即使foo<bool>没有定义专业化!

元程序还有一个static_assert波纹管来检查条件的评估是否成功:

static_assert( std::is_same<ok,foo<int>>::value , "Mmmmm..." );
Run Code Online (Sandbox Code Playgroud)

当我改变条件truefalse,我改变了测试,看看那里发生了什么:

static_assert( std::is_same<ok,foo<bool>>::value , "Mmmmm..." );
Run Code Online (Sandbox Code Playgroud)

元程序也通过了测试!即使有明确的instantation foo<bool>,并且ok,这应该是一个别名到该实例.
最后我检查了没有"没有匹配的专业化foo<bool>",直到我声明一个类型的变量ok:ok anok;

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

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

Fil*_*efp 6

很长的故事; 短.

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

std::is_same<ok, foo<bool>>::value;
Run Code Online (Sandbox Code Playgroud)

何时发生隐式实例化

14.7.1 隐式实例化 [templ.inst]

5) 如果在需要完全定义的对象类型的上下文中使用类类型,或者如果类类型的完整性可能影响程序的语义,则会隐式实例化类模板特化.

7) 如果需要隐式实例化类模板特化并且声明模板但未定义模板,则程序格式错误.

上述文本所说的是,只有在需要完全定义它的上下文中使用类模板时才会隐式实例化它,例如在声明所述模板的对象时,或者在尝试访问其中的某些内容时.

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


何时需要完全定义的对象?

标准在几个不同的位置引用了"完全定义",主要是当它明确表示需要这样的对象时.

最简单的定义何时需要一个完全定义的对象是通过阅读以下内容来解释它不是什么.

3.9p5 类型 [basic.types]

已声明但未定义的类,或未知大小或不完整元素类型的数组,是未完全定义的对象类型.未完全定义的对象类型和void类型是不完整的类型(3.9.1).不应将对象定义为具有不完整类型.

上面的措辞指出,只要我们不声明一个不完整类型的对象,我们就是明确的; 即.我们的模板不会被隐式实例化.

请参阅下面的示例,其中(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>`
Run Code Online (Sandbox Code Playgroud)