Templated delegating copy constructor in constant expressions

荒凉一梦 提交于 2020-03-21 17:55:34

问题


This question is motivated by this one.

Consider the following code:

struct B {};

struct S {
    B b; // #1

    S() = default;

    template <typename ...dummy> // #2
    constexpr S(const S&) {}

    template <typename ...dummy> // #3
    constexpr S(S &other) 
        : S(const_cast<const S&>(other)) // #4
    {}
};

S s;
constexpr S f() {return s;}

int main() {
    constexpr auto x = f();
}

GCC compiles this code successfully, but Clang rejects it (Example on Godbolt.org). The error message produced by Clang is

<source>:21:20: error: constexpr variable 'x' must be initialized by a constant expression
    constexpr auto x = f();
                   ^   ~~~
<source>:13:11: note: read of non-constexpr variable 's' is not allowed in a constant expression
        : S(const_cast<const S&>(other)) 
          ^
<source>:13:11: note: in call to 'S(s)'
<source>:18:25: note: in call to 'S(s)'
constexpr S f() {return s;}
                        ^
<source>:21:24: note: in call to 'f()'
    constexpr auto x = f();
                       ^
<source>:17:3: note: declared here
S s;
  ^

Note if we remove any of #2, #3 or #4, both compilers accept this code. If we replace #1 with int b = 0;, both compilers reject it.

My question is:

  1. Which compiler is correct according to the current standard?
  2. If GCC is correct, why does replacing #1 with int b = 0; make this code ill-formed? If Clang is correct, why does removing any of #2, #3 or #4 make this code well-formed?

回答1:


Since both your user-defined constructors are templates, they are not copy (or move) constructors. So the compiler implicitly declares a copy constructor, and defines it as defaulted.

Part 1 thus comes down to the following distinguishing program:

struct A {
    struct B {} b;
    constexpr A() {};
    // constexpr A(A const& a) : b{a.b} {}    // #1
};
int main() {
    auto a = A{};
    constexpr int i = (A{a}, 0);
}

Rejected by Clang and MSVC, accepted by gcc; uncomment #1 for all three to accept.

Per the definition of the implicitly-defined copy constructor there is no way that #1 is any different to constexpr A(A const&) = default; so gcc is correct. Note also that if we give B a user-defined constexpr copy constructor Clang and MSVC again accept, so the issue appears to be that these compilers are unable to track the constexpr copy constructibility of recursively empty implicitly copyable classes. Filed bugs for MSVC and Clang (fixed for Clang 11).

Part 2:

Removing #1 means that you are copying (performing lvalue-to-rvalue conversion on) an object s.b of type int, whose lifetime began outside constexpr context.

Removing #2 gives S a user-defined constexpr copy constructor, which is then delegated to at #4.

Removing #3 gives S a user-defined (non-const) copy constructor, suppressing the implicitly-defined copy constructor, so the delegating construction invokes the template const constructor (which, remember, is not a copy constructor).

Removing #4 means that your constructor template with argument S& other no longer calls the implicitly-defined copy constructor, so b is default-initialized, which Clang can do in constexpr context. Note that a copy constructor is still implicitly declared and defined as defaulted, it is just that your constructor template<class...> S::S(S& other) is preferred by overload resolution.

It is important to recognize the distinction between suppressing the implicitly-defined copy constructor and providing a preferred overload. template<class...> S::S(S&) does not suppress the implicitly-defined copy constructor, but it is preferred for non-const lvalue argument, assuming that the implicitly-defined copy constructor has argument S const&. On the other hand, template<class...> S::S(S const&) does not suppress the implicitly-defined copy constructor, and can never be preferred to the implicitly-defined copy constructor since it is a template and the parameter-lists are the same.



来源:https://stackoverflow.com/questions/60472257/templated-delegating-copy-constructor-in-constant-expressions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!