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?
TL;DR: Clang is highly compatible to GCC - just give it a go. In most cases, Clang could be used as a GCC drop in replacement ( clang and clang++ are "GCC compatible drivers").
A constructor that is declared with a constexpr specifier is a constexpr constructor. Previously, only expressions of built-in types could be valid constant expressions. With constexpr constructors, objects of user-defined types can be included in valid constant expressions.
Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With