c++ “成员函数外的封闭类定义中需要默认成员初始化器”-我的代码是不是格式错误?

zengzsys  于 2023-03-14  发布在  其他
关注(0)|答案(6)|浏览(123)
#include <utility>

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

int main()
{ 
}

live example on godbolt
上面的代码可以用我测试的任何版本的g编译,也可以用clang3.6到3.9.1编译,但是不能用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};
        ^

**我的代码是否格式错误?**如果是,错误的含义是什么?

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

uxhixvfz

uxhixvfz1#

在我看来,你的代码很好。Clang似乎在使用= default构造函数,而不仅仅是手动定义一个默认构造函数。它的源代码中有如下说明:
DR1351:如果非静态数据成员的大括号或等号初始化式调用其类或可能求值的子表达式中的封闭类的默认构造函数,则程序是格式错误的。
这个决议是行不通的:在未求值的上下文中,特别是在noexcept_expression的操作数中,可能需要缺省构造函数的异常说明,并且我们可能无法计算封闭类的异常说明。
在初始化式词法完整之前,任何试图解决默认构造函数异常规范的尝试最终都会出现在这里,在这里我们可以诊断它。
我个人认为它可能错误地拾取了错误。但是它特别提到了“默认的默认构造函数”。
以下方法似乎有效:

#include <utility>

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

int main()
{ 
}
2jcobegt

2jcobegt2#

这似乎与11月的提交有关。https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html
这一定是编译器的错误。有没有报告过?

xqnpmsa8

xqnpmsa83#

您的用法正常。

剩下的是什么?我猜是编译器错误。这个证据怎么样?在你的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仍然以同样的方式出错。不幸的是,我没有Clang 4. 0. 0可以在机器上测试,所以我不能肯定它是Clang还是Godbolt。

wlsrxk51

wlsrxk514#

正如C++ 11标准所述
第5.3.7节
noexcept运算符确定其操作数的求值是否会抛出异常(15.1),该操作数是一个未求值的操作数(子句5)。

noexcept-expression:
noexcept ( expression )

noexcept运算符的结果是bool类型的常量,并且是右值。如果在可能计算的上下文中表达式包含
-对一个函数、成员函数、函数指针或成员函数指针的潜在求值调用,没有非抛出异常规范(15.4),除非该调用是一个常量表达式(5.19),

  • 潜在求值的throw表达式(15.1),
  • 潜在求值的dynamic_cast表达式dynamic_cast(v),其中T是引用类型,需要运行时检查(5.2.7),或
  • 应用于类型是多态类类型(10.3)的glvalue表达式的潜在求值的typeid表达式(5.2.8)。
    否则,结果为真。
    以及
template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

add_rvalue_reference是转换特性类型,并且,没有明确地说,但不要求对象/函数定义被示例化。
从这里可以清楚地看到struct foo; ... noexcept(std::declval<foo>())是一个法律的的代码。顺便说一下,noexcept部分等价于noexcept(true)noexcept(true)等价于noexcept,而noexcept(noexcept没有意义,要获得构造函数noexcept说明符,你必须执行noexcept(foo())。后者也是有效的,但不幸的是,编译器无法处理它。可能是因为他们为非C11代码构建单元的顺序,而且他们还没有转换这个模型。这反映了您在特定libc实现中遇到的bug的性质。由于某些原因,add_rvalue_reference由于noexcept说明符的存在而尝试使用构造函数的声明,并且由于这发生在成员函数之外,正如前面提到的,它失败了。所以是的,这是库的一个bug。

iovurdzv

iovurdzv5#

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

#include <utility>

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

int main()
{ 
}
3okqufwl

3okqufwl6#

看起来这个问题在clang 6中得到了修复:https://gcc.godbolt.org/z/fzsvhxaP8(使用上面的litb的简化重现)。

相关问题