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

时间:2021-02-05 06:57:55

I was working on a simple solution to the common "Conditional on ill-formed types" problem (Like this yesterday question).

我正在研究一个简单的解决方案,解决常见的“有条件的不良类型”问题(就像昨天的问题一样)。

In my codebase I have a template to hold uninstanced templates and instance them later. Something like 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;

Where identity is the identity metafunction.
This could be used as follows:

身份是身份元功能的地方。这可以使用如下:

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

So the solution which comes to my mind is to wrap the two targets of the conditional on that way, and instantiate the result of the conditional, so only the selected template is instanced. For that purpose, I have modified lazy and impl::lazy_instance to allow us to pass the template parameters at the lazy instantation point:

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

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...>>
    {};
}

Note that in this case lazy_instance takes template parameters too, but they are ignored. I have donde this on that way to have the same interface for both usage cases.

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

So our main problem, the evaluation of conditional selection of potentially ill-formed types could be achieved as follows:

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

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

Where foo is a template where the bool instantation is ill-formed, for example:

其中foo是bool瞬时形成不良的模板,例如:

template<typename T>
struct foo;

template<>
struct foo<int>
{};

It seems to work, isn't? Great. But a couple of minutes later I changed the boolean flag to false, and surprisingly it works too! Even if foo<bool> specialization is not defined!

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

Also the metaprogram has a static_assert bellow to check if the evaluation of the conditional was successfull:

元节目还有一个static_assert,用于检查条件的评估是否成功:

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

When I changed the condition from true to false, I changed the test to see whats happening there:

当我将条件从true更改为false时,我更改了测试以查看在那里发生的事情:

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

And the metaprogram passes the test too! Even with the explicit instantation of foo<bool>, and ok, which should be an alias to that instance.
Finally I have checked that there is no "No matching specialization for foo<bool>" until I declare a variable of type ok: ok anok;

元程序也通过了测试!即使有foo 的显式即时,也好,这应该是该实例的别名。最后我检查了没有“foo 没有匹配的专业化”,直到我声明一个类型为ok的变量:ok anok;

I'm working with CLang 3.4, on C++11 mode (-std=c++11). My question is: Whats happening here? Is that behaviour correct or its a LLVM bug?

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

EDIT: Here is a SSCCE running at ideone. It uses GCC 4.8.1, but seems like it has the same behaviour.

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

1 个解决方案

#1


6  

Long story; short.

You are actually not instantiating foo<bool> in the following expression:

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

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

When does implicit instantiation occur?

14.7.1 Implicit instantiation [templ.inst]

14.7.1隐式实例化[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.

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

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.

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

What the above text says is that a class template is only implicitly instantiated when it is used in a context that requires it to be fully defined, such as when declaring an object of said template, or when trying to access something inside it.

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

Checking if one type is the same as another does not count as such context, since we are merely comparing two names.

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


When is a completely-defined object required?

The Standard makes a reference to "completely-defined" in several different locations, mostly when it explicitly says that such an object is required.

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

The easiest definition of when we need a completely-defined object is by reading the following, which explains what it isn't.

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

3.9p5 Types [basic.types]

3.9p5类型[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.

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

The wording above states that as long as we don't declare an object to be of an incomplete-type, we are in the clear; ie. our template will not be implicitly instantiated.

上面的措辞表明,只要我们不宣布某个对象属于不完整类型,我们就是明确的;即。我们的模板不会被隐式实例化。

See the below example where (C) and (D) tries to create an object of incomplete-type, both (A) and (B) are legal since they don't cause implicit instantiation.

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

#1


6  

Long story; short.

You are actually not instantiating foo<bool> in the following expression:

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

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

When does implicit instantiation occur?

14.7.1 Implicit instantiation [templ.inst]

14.7.1隐式实例化[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.

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

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.

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

What the above text says is that a class template is only implicitly instantiated when it is used in a context that requires it to be fully defined, such as when declaring an object of said template, or when trying to access something inside it.

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

Checking if one type is the same as another does not count as such context, since we are merely comparing two names.

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


When is a completely-defined object required?

The Standard makes a reference to "completely-defined" in several different locations, mostly when it explicitly says that such an object is required.

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

The easiest definition of when we need a completely-defined object is by reading the following, which explains what it isn't.

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

3.9p5 Types [basic.types]

3.9p5类型[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.

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

The wording above states that as long as we don't declare an object to be of an incomplete-type, we are in the clear; ie. our template will not be implicitly instantiated.

上面的措辞表明,只要我们不宣布某个对象属于不完整类型,我们就是明确的;即。我们的模板不会被隐式实例化。

See the below example where (C) and (D) tries to create an object of incomplete-type, both (A) and (B) are legal since they don't cause implicit instantiation.

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