Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incomplete type is not allowed in a class, but is allowed in a class template

The following is invalid code:

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

int main() { return foo{}.x.value; }

This is quite clear, as foo::bar is considered incomplete at the point where foo::x is defined.

However, there seems to be a "workaround" which makes the same class definition valid:

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

This works with all major compilers. I have three questions about this:

  1. Is this indeed valid C++ code, or just a quirk of the compilers?
  2. If it is valid code, is there a paragraph in the C++ standard that deals with this exception?
  3. If it is valid code, why is the first version (without template) considered invalid? If the compiler can figure out the second option, I don't see a reason why it wouldn't be able to figure out the first one.

If I add an explicit specialization for 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; } 

It once again fails to compile.

like image 558
gflegar Avatar asked May 06 '18 17:05

gflegar


Video Answer


2 Answers

The real answer might be ¯\_(ツ)_/¯, but it's probably currently okay because templates are magical, but it may be more explicitly not okay pending some other core issue resolutions.

First, the main problem of course is [class.mem]/14:

Non-static data members shall not have incomplete types.

This is why your non-template example is ill-formed. However, according to [temp.point]/4:

For a class template specialization, a class member template specialization, or a specialization for a class member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

Which suggests that foo_impl<void>::bar is instantiated before foo_impl<void>, and hence it's complete at the point where the non-static data member of type bar is instantiated. So maybe it's okay.

However, core language issues 1626 and 2335 deal with not-exactly-the-same-but-still-quite-similar issues regarding completeness and templates, and both point to desiring to make the template case more consistent with the non-template case.

What does all of this mean when viewed as a whole? I'm not sure.

like image 197
Barry Avatar answered Oct 20 '22 00:10

Barry


I think this example is explicitly allowed by

17.6.1.2 Member classes of class templates [temp.mem.class]

1 A member class of a class template may be defined outside the class template definition in which it is declared. [Note: The member class must be defined before its first use that requires an instantiation (17.8.1) For example,

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

—end note ]

This should work fine too:

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;
}
like image 32
user7860670 Avatar answered Oct 20 '22 00:10

user7860670