“在成员函数之外的封闭类的定义中需要默认成员初始值设定项” - 我的代码是不正确的?

时间:2023-01-15 16:47:21
#include <utility>

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

int main()
{ 
}

live example on godbolt

关于Godbolt的现实例子


The code above compiles with any version of g++ I tested, and with clang++ from 3.6 to 3.9.1, but does not compile with clang++ 4.0.0:

上面的代码编译了我测试的任何g ++版本,以及从3.6到3.9.1的clang ++,但不能用clang ++ 4.0.0编译:

test.cpp:6:5: error: default member initializer for 'x' needed within 
definition of enclosing class 'foo' outside of member functions
    foo() noexcept = default;
    ^
type_traits:126:26: note: in instantiation of template 
class 'std::is_function<foo &>' requested here
    : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
                        ^
type_traits:154:39: note: in instantiation of template 
class 'std::__or_<std::is_function<foo &>,
    std::is_reference<foo &>, std::is_void<foo &> >' requested here
    : public integral_constant<bool, !_Pp::value>
                                    ^
type_traits:598:14: note: in instantiation of template 
class 'std::__not_<std::__or_<std::is_function<foo &>,
    std::is_reference<foo &>, std::is_void<foo &> > >' requested here
    : public __not_<__or_<is_function<_Tp>, is_reference<_Tp>,
            ^
type_traits:121:26: note: in instantiation of template 
class 'std::is_object<foo &>' requested here
    : public conditional<_B1::value, _B1, _B2>::type
                        ^
type_traits:635:14: note: in instantiation of template 
class 'std::__or_<std::is_object<foo &>,
    std::is_reference<foo &> >' requested here
    : public __or_<is_object<_Tp>, is_reference<_Tp>>::type
            ^
type_traits:1667:33: note: in instantiation of template 
class 'std::__is_referenceable<foo &>' requested here
template<typename _Tp, bool = __is_referenceable<_Tp>::value>
                                ^
type_traits:1678:14: note: in instantiation of default 
argument for '__add_rvalue_reference_helper<foo &>'
    required here
    : public __add_rvalue_reference_helper<_Tp>
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type_traits:2267:12: note: in instantiation of template 
class 'std::add_rvalue_reference<foo &>' requested
    here
    inline typename add_rvalue_reference<_Tp>::type
        ^
wtfff.cpp:7:32: note: while substituting explicitly-specified 
template arguments into function template 'declval'
    void f() noexcept(noexcept(std::declval<foo&>())) {}
                            ^
wtfff.cpp:5:9: note: default member initializer declared here
    int x{0};
        ^

Is my code ill-formed? If so, what's the meaning of the error?

我的代码格式不正确吗?如果是这样,错误的含义是什么?

Note that removing the noexcept from the constructor or the {0} initializer from x will make the code compile.

请注意,从构造函数中删除noexcept或从x中删除{0}初始化程序将使代码编译。

5 个解决方案

#1


10  

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:

从我所知道的,你的代码很好。 Clang似乎很难使用= default构造函数,而不仅仅是手动定义默认构造函数。它的源代码中有以下spiel:

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.

DR1351:如果非静态数据成员的大括号或等于初始化程序在可能已评估的子表达式中调用其类的默认默认构造函数或封闭类,则该程序格式错误。

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.

此解决方案不可行:默认构造函数的异常规范可能需要在未评估的上下文中,特别是在noexcept-expression的操作数中,并且我们无法为封闭的类计算异常规范。

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()
{ 
}

#2


1  

This seems like it might be related to this commit in November. https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html

这似乎可能与11月的这次提交有关。 https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html

It must be a compiler bug. Has it been reported?

它必须是编译器错误。有报道吗?

#3


0  

Your usage is fine.

你的用法很好。

  • Your int x{0} clearly falls under the category non-static data member with initializer (C++11).
  • 您的int x {0}显然属于具有初始化程序的类别非静态数据成员(C ++ 11)。

  • The declval does not require a complete type--that's kind of the point of it, as explained in this nice exposition.
  • declval不需要一个完整的类型 - 这就是它的一点,正如在这个很好的阐述中所解释的那样。

What's left? Compiler bug, I guess. How about this for evidence? Use a complete type z instead of foo in your declval.

还剩下什么?编译错误,我想。证据如何?在你的declval中使用完整的类型z而不是foo。

#include <utility>
struct z{};
struct foo
{
    int x{0};
    foo() noexcept = default;
    void f() noexcept( noexcept( std::declval<z>() ) ) {}
};

int main()
{ 
}

Clang 4.0.0 on godbolt still errors the same way. Unfortunately I do not have clang 4.0.0 available on a machine to test, so I cannot not say whether it is Clang or godbolt for certain.

Godbolt上的Clang 4.0.0仍然以同样的方式出错。不幸的是我没有在机器上使用clang 4.0.0进行测试,所以我不能不确定它是Clang还是godbolt。

#4


0  

As C++ 11 standard states

正如C ++ 11标准所述

§ 5.3.7

The noexcept operator determines whether the evaluation of its operand, which is an unevaluated operand (Clause 5), can throw an exception (15.1).

noexcept运算符确定对其操作数(即未评估的操作数(第5条))的求值是否可以抛出异常(15.1)。

noexcept-expression:
noexcept ( expression )

The result of the noexcept operator is a constant of type bool and is an rvalue. The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain

noexcept运算符的结果是bool类型的常量,是一个rvalue。如果在表达式包含的潜在评估上下文中,则noexcept运算符的结果为false

a potentially evaluated call to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4), unless the call is a constant expression (5.19),

- 对函数,成员函数,函数指针或成员函数指针的潜在评估调用,它没有非抛出异常规范(15.4),除非调用是常量表达式(5.19),

— a potentially evaluated throw-expression (15.1),

- 一个可能被评估的throw-expression(15.1),

— a potentially evaluated dynamic_cast expression dynamic_cast(v), where T is a reference type, that requires a run-time check (5.2.7), or

- 可能评估的dynamic_cast表达式dynamic_cast(v),其中T是引用类型,需要运行时检查(5.2.7),或者

— a potentially evaluated typeid expression (5.2.8) applied to a glvalue expression whose type is a polymorphic class type (10.3).

- 应用于glvalue表达式的潜在评估的typeid表达式(5.2.8),其类型是多态类类型(10.3)。

Otherwise, the result is true.

否则,结果是真的。

and

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

The add_rvalue_reference is of type Transformation Trait and, not explicitly said, but does not require object/function definition to be instantiated.

add_rvalue_reference的类型为Transformation Trait,并未明确说明,但不需要实例化对象/函数定义。

From here it is clear that struct foo; ... noexcept(std::declval<foo>()) is a legal code. Where noexcept part, by the way, is equivalent to noexcept(true) that is equivalent to just noexcept, and noexcept(noexcept does not make sense, to get a constructor noexcept specifier you have to do this noexcept(foo()). The latter is valid as well, but, unfortunately, compilers are not able to deal with it, probably because of the order how they build the unit for non C++11 code, and they have not transformed this model yet. This reflects the nature of the bug that you encounter in the particular libc++ implementation. For some reasons add_rvalue_reference due to the presence of noexcept specifier tries to use declaration of the constructor and since that happens outside of the member function, as mentioned before, it fails. So yes, this is a bug of the library.

从这里可以清楚地看到struct foo; ... noexcept(std :: declval ())是一个合法的代码。顺便说一下,noexcept部分等同于noexcept(true)等于noexcept,而noexcept(noexcept没有意义,获取构造函数noexcept说明符,你必须这样做noexcept(foo())。后者也是有效的,但不幸的是,编译器无法处理它,可能是因为他们为非C ++ 11代码构建单元的顺序,他们还没有改变这个模型。这反映了性质您在特定的libc ++实现中遇到的错误。由于某些原因,由于存在noexcept说明符而导致add_rvalue_reference尝试使用构造函数的声明,并且由于这发生在成员函数之外,如前所述,它会失败。所以是的,这是图书馆的一个错误。

#5


0  

This is correct syntactical way is I can say.

这是正确的语法方式,我可以说。

#include <utility>

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

int main()
{ 
}

#1


10  

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:

从我所知道的,你的代码很好。 Clang似乎很难使用= default构造函数,而不仅仅是手动定义默认构造函数。它的源代码中有以下spiel:

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.

DR1351:如果非静态数据成员的大括号或等于初始化程序在可能已评估的子表达式中调用其类的默认默认构造函数或封闭类,则该程序格式错误。

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.

此解决方案不可行:默认构造函数的异常规范可能需要在未评估的上下文中,特别是在noexcept-expression的操作数中,并且我们无法为封闭的类计算异常规范。

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()
{ 
}

#2


1  

This seems like it might be related to this commit in November. https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html

这似乎可能与11月的这次提交有关。 https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html

It must be a compiler bug. Has it been reported?

它必须是编译器错误。有报道吗?

#3


0  

Your usage is fine.

你的用法很好。

  • Your int x{0} clearly falls under the category non-static data member with initializer (C++11).
  • 您的int x {0}显然属于具有初始化程序的类别非静态数据成员(C ++ 11)。

  • The declval does not require a complete type--that's kind of the point of it, as explained in this nice exposition.
  • declval不需要一个完整的类型 - 这就是它的一点,正如在这个很好的阐述中所解释的那样。

What's left? Compiler bug, I guess. How about this for evidence? Use a complete type z instead of foo in your declval.

还剩下什么?编译错误,我想。证据如何?在你的declval中使用完整的类型z而不是foo。

#include <utility>
struct z{};
struct foo
{
    int x{0};
    foo() noexcept = default;
    void f() noexcept( noexcept( std::declval<z>() ) ) {}
};

int main()
{ 
}

Clang 4.0.0 on godbolt still errors the same way. Unfortunately I do not have clang 4.0.0 available on a machine to test, so I cannot not say whether it is Clang or godbolt for certain.

Godbolt上的Clang 4.0.0仍然以同样的方式出错。不幸的是我没有在机器上使用clang 4.0.0进行测试,所以我不能不确定它是Clang还是godbolt。

#4


0  

As C++ 11 standard states

正如C ++ 11标准所述

§ 5.3.7

The noexcept operator determines whether the evaluation of its operand, which is an unevaluated operand (Clause 5), can throw an exception (15.1).

noexcept运算符确定对其操作数(即未评估的操作数(第5条))的求值是否可以抛出异常(15.1)。

noexcept-expression:
noexcept ( expression )

The result of the noexcept operator is a constant of type bool and is an rvalue. The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain

noexcept运算符的结果是bool类型的常量,是一个rvalue。如果在表达式包含的潜在评估上下文中,则noexcept运算符的结果为false

a potentially evaluated call to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4), unless the call is a constant expression (5.19),

- 对函数,成员函数,函数指针或成员函数指针的潜在评估调用,它没有非抛出异常规范(15.4),除非调用是常量表达式(5.19),

— a potentially evaluated throw-expression (15.1),

- 一个可能被评估的throw-expression(15.1),

— a potentially evaluated dynamic_cast expression dynamic_cast(v), where T is a reference type, that requires a run-time check (5.2.7), or

- 可能评估的dynamic_cast表达式dynamic_cast(v),其中T是引用类型,需要运行时检查(5.2.7),或者

— a potentially evaluated typeid expression (5.2.8) applied to a glvalue expression whose type is a polymorphic class type (10.3).

- 应用于glvalue表达式的潜在评估的typeid表达式(5.2.8),其类型是多态类类型(10.3)。

Otherwise, the result is true.

否则,结果是真的。

and

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

The add_rvalue_reference is of type Transformation Trait and, not explicitly said, but does not require object/function definition to be instantiated.

add_rvalue_reference的类型为Transformation Trait,并未明确说明,但不需要实例化对象/函数定义。

From here it is clear that struct foo; ... noexcept(std::declval<foo>()) is a legal code. Where noexcept part, by the way, is equivalent to noexcept(true) that is equivalent to just noexcept, and noexcept(noexcept does not make sense, to get a constructor noexcept specifier you have to do this noexcept(foo()). The latter is valid as well, but, unfortunately, compilers are not able to deal with it, probably because of the order how they build the unit for non C++11 code, and they have not transformed this model yet. This reflects the nature of the bug that you encounter in the particular libc++ implementation. For some reasons add_rvalue_reference due to the presence of noexcept specifier tries to use declaration of the constructor and since that happens outside of the member function, as mentioned before, it fails. So yes, this is a bug of the library.

从这里可以清楚地看到struct foo; ... noexcept(std :: declval ())是一个合法的代码。顺便说一下,noexcept部分等同于noexcept(true)等于noexcept,而noexcept(noexcept没有意义,获取构造函数noexcept说明符,你必须这样做noexcept(foo())。后者也是有效的,但不幸的是,编译器无法处理它,可能是因为他们为非C ++ 11代码构建单元的顺序,他们还没有改变这个模型。这反映了性质您在特定的libc ++实现中遇到的错误。由于某些原因,由于存在noexcept说明符而导致add_rvalue_reference尝试使用构造函数的声明,并且由于这发生在成员函数之外,如前所述,它会失败。所以是的,这是图书馆的一个错误。

#5


0  

This is correct syntactical way is I can say.

这是正确的语法方式,我可以说。

#include <utility>

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

int main()
{ 
}