类中不允许使用不完整类型,但允许在类模板中使用

Gor*_*gar 21 c++ templates forward-declaration language-lawyer c++11

以下是无效代码:

struct foo {
    struct bar;
    bar x;        // error: field x has incomplete type
    struct bar{ int value{42}; };
};

int main() { return foo{}.x.value; }
Run Code Online (Sandbox Code Playgroud)

这很清楚,因为foo::barfoo::x定义的点上被认为是不完整的.

但是,似乎有一个"解决方法"使相同的类定义有效:

template <typename = void>
struct foo_impl {
    struct bar;
    bar x;        // no problems here
    struct bar{ int value{42}; };
};

using foo = foo_impl<>;

int main() { return foo{}.x.value; }
Run Code Online (Sandbox Code Playgroud)

适用于所有主要编译器.我有三个问题:

  1. 这确实是有效的C++代码,还是只是编译器的一个怪癖?
  2. 如果它是有效的代码,C++标准中是否有一个处理此异常的段落?
  3. 如果它是有效代码,为什么第一个版本(不template)被认为是无效的?如果编译器可以找出第二个选项,我没有看到为什么它无法找出第一个选项的原因.

如果我添加一个明确的专业化void:

template <typename = void>
struct foo_impl {};

template<>
struct foo_impl<void> {
    struct bar;
    bar x;        // error: field has incomplete type
    struct bar{ int value{42}; };
};

using foo = foo_impl<>;

int main() { return foo{}.x.value; } 
Run Code Online (Sandbox Code Playgroud)

它再次无法编译.

Bar*_*rry 6

真正的答案可能是¯\ _(ツ)_ /¯,但它可能目前还可以,因为模板很神奇,但在某些其他核心问题解决方案之前可能更明确地不行.

首先,主要问题当然是[class.mem]/14:

非静态数据成员不应具有不完整的类型.

这就是您的非模板示例格式错误的原因.但是,根据[temp.point]/4:

对于类模板特化,类成员模板特化或类模板的类成员的特化,如果特化是隐式实例化的,因为它是从另一个模板特化中引用的,如果引用特化的上下文取决于在模板参数上,如果在封闭模板的实例化之前未实例化特化,则实例化的点紧接在封闭模板的实例化之前.否则,这种特化的实例化点紧接在引用特化的命名空间范围声明或定义之前.

这表明之前foo_impl<void>::bar已实例化,因此它在实例化类型的非静态数据成员时完成.所以也许没关系. foo_impl<void>bar

但是,核心语言问题16262335处理的是关于完整性和模板的不完全相同但仍然非常相似的问题,并且都指向希望使模板案例与非模板案例更加一致.

从整体上看,所有这些意味着什么?我不确定.


use*_*670 5

我认为这个例子是明确允许的

17.6.1.2类模板的成员类[temp.mem.class]

1类模板的成员类可以在声明它的类模板定义之外定义.[注意:成员类必须在首次使用之前定义,需要实例化(17.8.1)例如,

template<class T> struct A {
  class B;
};

A<int>::B* b1; // OK: requires A to be defined but not A::B
template<class T> class A<T>::B { };
A<int>::B b2; // OK: requires A::B to be defined
Run Code Online (Sandbox Code Playgroud)

- 尾注]

这应该也可以正常工作:

template <typename = void>
struct foo_impl {
    struct bar;
    bar x;        // no problems here
};

template<typename T>
struct foo_impl<T>::bar{ int value{42}; };

using foo = foo_impl<>;

int main()
{
    return foo{}.x.value;
}
Run Code Online (Sandbox Code Playgroud)