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

时间:2017-05-06 10:26:53

标签: c++ c++11 language-lawyer

#include <utility>

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

int main()
{ 
}

live example on godbolt

上面的代码编译了我测试的任何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};
        ^

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

请注意,从noexcept中删除构造函数中的{0}x初始值设定项将使代码编译。

5 个答案:

答案 0 :(得分:10)

根据我的判断,你的代码很好。 Clang似乎很难与= default构造函数斗争,而不仅仅是手动定义默认构造函数。它的源代码中包含以下内容:

  

DR1351:     如果是非静态数据成员的brace-or-equal-initializer     调用其类或一个默认的默认构造函数     将类封闭在一个可能被评估的子表达式中     节目形成不良。

     

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

     

任何尝试解决默认默认值的异常规范   初始化程序在词法上完成之前的构造函数将最终完成   来到这里我们可以诊断它。

我认为个人可能错误地接收错误。但它特别提到“默认默认构造函数”。

以下似乎有效:

#include <utility>

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

int main()
{ 
}

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

你的用法很好。

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

#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。

答案 3 :(得分:0)

正如C ++ 11标准所述

  

§5.3.7

     

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

noexcept-expression:
noexcept ( expression )
     

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

     

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

     

- 潜在评估的throw-expression(15.1),

     

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

     

- 应用于类型为a的glvalue表达式的可能评估的typeid表达式(5.2.8)   多态类型(10.3)。

     

否则,结果为真。

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

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

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

答案 4 :(得分:0)

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

#include <utility>

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

int main()
{ 
}