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

前端 未结 5 1034
别那么骄傲
别那么骄傲 2020-12-05 02:50
#include 

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

int main()
         


        
相关标签:
5条回答
  • 2020-12-05 03:08

    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()
    { 
    }
    
    0 讨论(0)
  • 2020-12-05 03:08

    Your usage is fine.

    • Your int x{0} clearly falls under the category non-static data member with initializer (C++11).
    • The declval does not require a complete type--that's kind of the point of it, as explained in this nice exposition.

    What's left? Compiler bug, I guess. How about this for evidence? Use a complete type z instead of foo in your 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 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.

    0 讨论(0)
  • 2020-12-05 03:23

    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()
    { 
    }
    
    0 讨论(0)
  • 2020-12-05 03:24

    As C++ 11 standard states

    § 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-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

    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),

    — a potentially evaluated 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

    — a potentially evaluated typeid expression (5.2.8) applied to a glvalue expression whose type is a polymorphic class type (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.

    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.

    0 讨论(0)
  • 2020-12-05 03:34

    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

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

    0 讨论(0)
提交回复
热议问题