简体   繁体   中英

GCC and Clang different behaviors on constexpr constructor

For this struct:

struct Wrapper {
    int value;

    constexpr explicit Wrapper(int v) noexcept : value(v) {}
    Wrapper(const Wrapper& that) noexcept : value(that.value) {}
};

And this function:

constexpr Wrapper makeWrapper(int v)
{
    return Wrapper(v);
}

The following code fails to compile for Clang (Apple LLVM version 7.3.0), but compiles fine for GCC (4.9+), both with -Wall -Wextra -Werror -pedantic-errors :

constexpr auto x = makeWrapper(123);

Clang complains that "non-constexpr constructor 'Wrapper' cannot be used in a constant expression." Which compiler is right?

Although the copy or move when returning Wrapper from makeWrapper() can be elided, it is required to exist with C++14. The existing copy constructor is non- constexpr and its existence inhibits creation of an implicit move constructor. As a result I think clang is right: you'd need to make the copy constructor a constexpr .

Note that with C++17 the code might become correct: there is a proposal to make copy-elision mandatory in some contexts: P0135r0 . However, it seems this change hasn't landed in the working paper, yet. It may land this week, though (thanks to @NicolBolas for pointing out that it isn't there, yet). I haven't seen an updated paper in the mailing .

Clang is correct. It works in g++ because it's automatically eliding the Copy constructor (RVO). if you pass -fno-elide-constructors . g++ will also complain.

The C++14 standard isn't clear about Copy-Elision in constexpr objects..

[class.copy/32] ... (partially reproduced here)

When the criteria for elision of a copy/move operation are met.... .... the selected constructor must be accessible even if the call is elided.

Until we know the definition of accessible ? We can assume g++ is also correct?

dcl.constexpr/9

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression ([expr.const]). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression.

Dietmar Kuhl's answer tells us what's ahead.

Demo:

struct Wrapper {
    int value;

    constexpr explicit Wrapper(int v) noexcept : value(v) {}
    Wrapper(const Wrapper& that) noexcept : value(that.value)  {}
};

constexpr Wrapper makeWrapper(int v)
{
    return Wrapper(v);
}

int main()
{
    constexpr auto x = makeWrapper(123);
}

Compile with

g++ -std=c++14 -Wall -pedantic -fno-elide-constructors main.cpp && ./a.out

See it live here

Both compilers are right.

The rules for constexpr functions and initialisers say that no non- constexpr function can be invoked.

The rules for copy elision say that it's unspecified whether the non- constexpr copy constructor gets invoked.

The only conclusion can be that it's unspecified whether the function and initialiser meet the requirements of constexpr . If they do, then the compiler must accept it. If they don't, then the compiler must diagnose the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM