"Default Member Initializer Needed Within Definition of Enclosing Class Outside of Member Functions" - Is My Code Ill-Formed

Default member initializer needed within definition of enclosing class outside of member functions - is my code ill-formed?

Your code is fine from what I can tell. Clang seems to struggle with the = default constructor rather than just defining a default constructor manually. It has the following spiel in its source code about it:

DR1351:
If the brace-or-equal-initializer of a non-static data member
invokes a defaulted default constructor of its class or of an
enclosing class in a potentially evaluated subexpression, the
program is ill-formed.

This resolution is unworkable: the exception specification of the
default constructor can be needed in an unevaluated context, in
particular, in the operand of a noexcept-expression, and we can be
unable to compute an exception specification for an enclosed class.

Any attempt to resolve the exception specification of a defaulted default
constructor before the initializer is lexically complete will ultimately
come here at which point we can diagnose it.

I think it may be incorrectly picking up the error, personally. But it specifially mentions "defaulted default constructor".

The following seems to work:

#include <utility>

struct foo
{
int x{0};
foo() noexcept {} // = default;
void f() noexcept(noexcept(std::declval<foo&>())) {}
};

int main()
{
}

Try to understand compiler error message: default member initializer required before the end of its enclosing class

This is a clang and gcc bug, we have a clang bug report for this: default member initializer for 'm' needed within definition of enclosing class for default argument of function which has the following example:

#include <limits>
class A
{
public:
class B
{
public:
explicit B() = default;
~B() = default;

private:
double m = std::numeric_limits<double>::max();
};

void f(double d, const B &b = B{}) {}
};

int main()
{
A a{};
a.f(0.);
}

which produces the following similar diagnostic:

t.cpp(15,34):  error: default member initializer for 'm' needed within definition of enclosing class 'A' outside of member functions
void f(double d, const B &b = B{}) {}
^
t.cpp(12,20): note: default member initializer declared here
double m = std::numeric_limits<double>::max();
^

Richard Smith indicates this is a bug:

Regarding comment#0: if we want to fix this once-and-for-all, we should use the same technique we use for delayed template parsing: teach Sema to call back into the parser to parse the delayed regions on-demand. Then we would only reject the cases where there's an actual dependency cycle.

Although does not explain why in details.

Error when using in-class initialization of non-static data member and nested class constructor

Is this code really incorrect or are the compilers wrong?

Well, neither. The standard has a defect -- it says both that A is considered complete while parsing the initializer for B::i, and that B::B() (which uses the initializer for B::i) can be used within the definition of A. That's clearly cyclic. Consider this:

struct A {
struct B {
int i = (A(), 0);
};
A() noexcept(!noexcept(B()));
};

This has a contradiction: B::B() is implicitly noexcept iff A() does not throw, and A() does not throw iff B::B() is not noexcept. There are a number of other cycles and contradictions in this area.

This is tracked by core issues 1360 and 1397. Note in particular this note in core issue 1397:

Perhaps the best way of addressing this would be to make it ill-formed for a non-static data member initializer to use a defaulted constructor of its class.

That's a special case of the rule that I implemented in Clang to resolve this issue. Clang's rule is that a defaulted default constructor for a class cannot be used before the non-static data member initializers for that class are parsed. Hence Clang issues a diagnostic here:

    A(const B& _b = B())
^

... because Clang parses default arguments before it parses default initializers, and this default argument would require B's default initializers to have already been parsed (in order to implicitly define B::B()).

Empty nested initializer list in C++

Both compilers are correct. Unless you violate a rule that says no diagnostic required the compiler should issue you a message. Whether that message is a warning or an error is up to the implementation. Normally you'll get a warning if it is something the compiler can still proceed with and an error when there is no way the compiler can continue.

clang 5: std::optional instantiation screws std::is_constructible trait of the parameter type

This looks like a compiler bug. From [class]

A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.

Which means Victim is complete at std::optional<Victim>, making it no different than any other type in this context.

From [meta]

The predicate condition for a template specialization is_­constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(declval<Args>()...);

Which is direct-initializing t with arguments of type Args..., or if sizeof...(Args) == 0, it's value-initializing t.

In this case, value-initializing t is to default-initialize t, which is valid hence std::is_constructible_v<Victim> should be true.

With all that said, compilers seems to be struggling a lot compiling this.

Reference member variable initialization error with default constructor

why defining a default constructor throws compilation error?

It's not that you define a default constructor, it's that the default constructor's definition doesn't initialize i. You are required to initialize all member variables that are references, and your empty definition does not do that.

This is for the same reason that you are required to initialize reference variables:

void foo() {
int &i; // error: declaration of reference variable 'i' requires an initializer
}

why would the compiler allow declaration of default constructor(without definition)

Because the definition is the problem, not the declaration. For example, moving the ill-formed constructor definition outside of the class definition will yield the same error:

class SomeClass
{
public:
SomeClass();
int &i;
};

SomeClass::SomeClass() {} // error: constructor for 'SomeClass' must explicitly initialize the reference member 'i'

The only problem with both examples is that you're not initializing i.

Take note of the following examples, which will compile. Note that the constructor's declaration does not change, but the definition does initialize i.

int someGlobalInt;

class SomeClass
{
public:
SomeClass() : i(someGlobalInt) {}
int &i;
};

int someGlobalInt;

class SomeClass
{
public:
SomeClass();
int &i;
};

SomeClass::SomeClass() : i(someGlobalInt) {}

According to my interpretation of §3.4.1/8 this code should compile. What am I missing?

The way you are using it does not fall under any of the conditions in [basic.lookup.unqual]/8, it is not used:

  • in a member function body
  • in a default argument
  • in an exception-specification
  • in the brace-or-equal-initializer of a non-static data member

and it does not fall under this case either:

definition of a class member outside of the definition of X

the following example shows some cases that do follow those rules:

struct C {
int y = I() ; // brace-or-equal-initializer of non-static data member
void f(int x = I()) // default argument
noexcept(sizeof(I)<4) // exception-specification
{
I i = x ; // member function body
}
using I = int;
};

How to implement full specialization templates member function within partial specialization templates class

[temp.expl.spec]/17:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition.

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.

for your code:

template<typename A, typename B>
class Foo{
public:
template<typename C> void bar();
};

template<typename T> // for `Foo`
template<> // for `bar`
inline void Foo<T, int>::bar<float>(){}

int main(){
Foo<double, int> foo;
foo.bar<float>();
return 0;
}

it's ill-formed because bar is explicitly specialized but Foo is NOT.



Related Topics



Leave a reply



Submit