我正在研究一个简单的解决方案,以解决常见的“条件错误的类型”(就像昨天的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也采用模板参数,但它们将被忽略。我以这种方式使用了donde,以使两个用例具有相同的界面。

因此,我们的主要问题是对潜在病态类型的条件选择进行评估的方法如下:
using ok = lazy_instance<typename std::conditional<true,
                                                   lazy<foo,int>,
                                                   lazy<foo,bool>
                                                  >::type
                        >;

其中foobool实例格式不正确的模板,例如:
template<typename T>
struct foo;

template<>
struct foo<int>
{};

似乎可行,不是吗?大。但是几分钟后,我将 bool(boolean) 标志更改为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模式(-std=c++11)上使用CLang 3.4。我的问题是:这是怎么回事?这是正确的行为还是LLVM错误?

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

最佳答案

很长的故事;短。

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

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

隐式实例化何时发生?



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

因为我们只是比较两个名称,所以检查一种类型是否与另一种类型相同并不视为这种上下文。

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

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

当我们需要一个完全定义的对象时,最简单的定义是阅读以下内容,这说明了不需要什么。



上面的措辞指出,只要我们不声明对象是不完整类型,我们就很清楚。即。我们的模板不会被隐式实例化。

请参见下面的示例,其中(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>`

09-10 00:07
查看更多