`constexpr` variable "used in its own initializer": Clang vs. GCC - c++

This question seems related to an existing one, but I do not understand the "portable workaround" provided in the answer there (involving const auto this_ = this;) and moreover I think the following example is easier to follow.
I am playing with the following snippet of C++17 code (live demo):
#include <iostream>
struct Test {
const char* name_{nullptr};
const Test* src_{nullptr};
constexpr Test(const char* name) noexcept
: name_{name}
{}
constexpr Test(const Test& src) noexcept
: src_{&src}
{
name_ = src_->name_;
src_ = nullptr;
}
};
template<char c>
void print_constexpr_char() {
std::cout << c << std::endl;
}
int main() {
constexpr const char* in = "x";
constexpr auto foo = Test{in};
constexpr auto bar = Test{foo};
std::cout << bar.name_ << std::endl;
print_constexpr_char<bar.name_[0]>();
return 0;
}
Compilation fails with GCC 7.2 while Clang 5.0.0 does not see any problem. The GCC error essentially reads
error: the value of 'bar' is not usable in a constant expression
note: 'bar' used in its own initializer
I am even more confused after realizing that removing the final print_constexpr_char makes the code compile although it still contains the line constexpr auto bar = Test{foo}; which GCC used to complain about ("used in its own initializer").
Which compiler is correct here?
How to understand the GCC note (if not a bug) that "using in its own initializer" is harmful iff the result is subsequently used in a constant expression?
Is there a valid way/workaround to use pointers in a constexpr constructor as an intermediate stage before transforming the object under construction into the final state which can be stored in a constexpr variable?

GCC is correct to reject the code (however the error message could use some work). You cannot use the address of a variable in a constant expression unless that variable has static storage duration.
foo is not static. If you move it outside of main, things will work. Demo
The line marked below is the problem:
constexpr Test(const Test& src) noexcept
: src_{&src} <--- That
Standard reference: (Emphasis mine)
[expr.const]
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted
result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies
the following constraints:
(5.2) — if the value is of pointer type, it contains the address of an object with static storage duration, the
address past the end of such an object (8.7), the address of a function, or a null pointer value,

Related

Clang fails with "static_assert expression is not an integral constant expression" for a static constexpr method of an object within another class [duplicate]

Accessing static class member functions or variables, can be done in two ways: through an object (obj.member_fun() or obj.member_var) or through the class (Class::member_fun() or Class::member_var). However, in constexpr functions, Clang gives an error on the object access and requires to use class access:
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
#define TEST 1
constexpr auto foo(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
#else
constexpr auto v = S::v(); // OK for clang and gcc
#endif
return v;
}
constexpr auto bar(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
#else
constexpr auto v = S::s_v; // OK for clang and gcc
#endif
return v;
}
int main() {}
Live Example compiled with -std=c++1z and #define TEST 1 for Clang 5.0 SVN, with error message:
Start
prog.cc:12:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
^~~~~
prog.cc:22:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
^~~~~
2 errors generated.
1
Finish
Question: is this is a Clang bug, or is gcc too liberal in accepting both syntax forms for static member access in a constexpr function?
Clang seems to be in the right. When accessing a static member with the member access syntax [class.static/1]:
A static member s of class X may be referred to using the qualified-id
expression X​::​s; it is not necessary to use the class member access
syntax to refer to a static member. A static member may be referred to
using the class member access syntax, in which case the object
expression is evaluated.
So s.v() will cause s to be evaluated. Now, according to [expr.const/2.11], s is not a constant expression:
2 An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference
type unless the reference has a preceding initialization and either:
(2.11.1) - it is initialized with a constant expression or
(2.11.2) - its lifetime began within the evaluation of e;
s doesn't have a preceding initialization with a constant expression, not in the scope of foo.
If you want to access the static members based of a function parameter, without hard-coding the type, the way forward is std::remove_reference_t<decltype(s)>. This is accepted by Clang and GCC both:
#include <type_traits>
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
constexpr auto foo(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::v();
return v;
}
constexpr auto bar(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::s_v;
return v;
}
int main() {}
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
I guess it depends on whether you compile in C++11 or C++14 mode. If you look over at cppreference, you will find (emphasis added by me):
A core constant expression is any expression that does not have any one of the following
(...)
6) The this pointer, except if used for class member access inside a non-static member function (until C++14)
6) The this pointer, except in a constexpr function or a constexpr constructor that is being evaluated as part of the expression (since C++14)
So, in C++11, whatever happens inside s.v() would not be considered a constant expression, since it uses the this pointer, but it is not a non-static member function (it's static) accessing a class member.
Per C++14, however, it would be, since it is evaluating a constexpr function as part of the expression, so the "except if" clause on the "does not have any of" set of rules catches.
Now don't ask me whether that makes any sense or whether anyone is supposed to understand that... :-)

Should this be a constexpr or not?

Consider this code snippet (godbolt):
#include <cstdio>
#include <string>
#include <string_view>
struct Option
{
std::string_view name;
constexpr Option( std::string_view const n ) noexcept : name{n} {}
};
template< std::size_t N >
class TransformedOption : public Option
{
public:
constexpr TransformedOption( std::string_view const nameStr ) :
Option{ { nameStorage_, N - 1 } }
{
for ( auto i = 0U; i < N; ++i )
{
if ( nameStr[ i ] == '_' ) { nameStorage_[ i ] = '-'; }
else { nameStorage_[ i ] = nameStr[ i ]; }
}
}
private:
char nameStorage_[ N ] = {};
};
template< std::size_t N >
constexpr TransformedOption< N > make( char const (&nameStr)[ N ] ) noexcept
{
return TransformedOption< N >{ nameStr };
}
int main()
{
/*constexpr*/ auto t = make( "abcd_efgh_ijkl_mnop_peqst" );
std::printf( "%s\n", t.name.data() );
return 0;
}
Basically, I want to perform compile-time string transformation by replacing each _ with - and making sure that the final binary contains only the transformed string (not the original).
I've tried Clang 10.0.1, GCC 10.2 and MSVC 19.24 (see above godbolt link). The weird stuff the following:
if constexpr is commented-out in main, then MSVC generates incorrect code (i.e. runtime transformation of string), but both GCC and clang generate correct code (i.e. transformed string constant is embedded into the assembly)
if constexpr is not commented-out in main, then MSVC generates correct code (i.e. transformed string constant is embedded into the assembly), but both GCC and clang fail to compile the code, stating that t is not initialized by constant expression (see godbolt). The weirdest thing is the GCC error message, which outputs the transformed string in its error and states that it's not a constant expression.
Well, which compiler is right, according to the C++ standard? To whom should I report a bug? To GCC and Clang folks or to Microsoft?
The constexpr declaration works in all compilers when t is also declared static.
constexpr static auto t = make( "abcd_efgh_ijkl_mnop_peqst" );
The reason is the string_view. It's a reference type that refers into the object being initialized. So one way or another, you are initializing a contexpr pointer. Now, a constexpr pointer (that is not initialized to a null pointer) may only be initialized with the address of an object with static storage duration.
[expr.const] (emphasis mine)
11 A constant expression is either a glvalue core constant
expression that refers to an entity that is a permitted result of a
constant expression (as defined below), or a prvalue core constant
expression whose value satisfies the following constraints:
if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted
result of a constant expression,
if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
an object ([expr.add]), the address of a non-immediate function, or a
null pointer value,
if the value is of pointer-to-member-function type, it does not designate an immediate function, and
if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is a permitted result of a constant expression if it is an
object with static storage duration that either is not a temporary
object or is a temporary object whose value satisfies the above
constraints, or if it is a non-immediate function.
When you declare the object to be of automatic storage duration, the pointer in the string_view is not initialized with the address of a static object. Hence GCC and Clang rightfully complain.
The self reference is what makes this interesting and tricky.

Non-constexpr variable sometimes usable in a constexpr context?

Take a look at the following code example:
template<bool val>
struct test {
static const int value_a = val;
const int value_b = val;
constexpr int get_value_a() const noexcept { return value_a; }
constexpr int get_value_b() const noexcept { return value_b; }
};
int main(int argc, char** argv) {
auto t = test<true>{};
static_assert(t.value_a);
// static_assert(t.value_b);
static_assert(t.get_value_a());
// static_assert(t.get_value_b());
}
Both gcc and clang agree that this should compile, but including any of the commented out static asserts makes it invalid. For example, gcc would then produce these error messages:
error: non-constant condition for static assertion
error: the value of ‘t’ is not usable in a constant expression
note: ‘t’ was not declared ‘constexpr’
This makes perfect sense to me and is exactly what I would have thought. But I don't really know why the other two static asserts compile in the first place. What is the relevant paragraph from the standard that allows this?
In particular, how is this formalized? Is there a clearly defined difference between just using a variable, versus actually accessing its runtime value (which then would be the only forbidden thing in a constexpr context)?
It's just the rules. Before constexpr, const variables initialised with constant expressions could be used as constant expressions themselves (Also for some compatibility with C)
From the standard [expr.const]/3:
A variable is usable in constant expressions after its initializing declaration is encountered if [...] it is a constant-initialized variable [...] of const-qualified integral or enumeration type.
This wouldn't extend to const auto t = test<true>{} because test<true> is not an integral type (You would need to have constexpr auto t = test<true>{}, as expected, following the rules of the rest of that paragraph)
In particular, how is this formalized?
http://eel.is/c++draft/expr.const#4.1
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of e;
Access to non-static members evaluates the this pointer. Access to a static member does not.

Static member access in constant expressions

Accessing static class member functions or variables, can be done in two ways: through an object (obj.member_fun() or obj.member_var) or through the class (Class::member_fun() or Class::member_var). However, in constexpr functions, Clang gives an error on the object access and requires to use class access:
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
#define TEST 1
constexpr auto foo(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
#else
constexpr auto v = S::v(); // OK for clang and gcc
#endif
return v;
}
constexpr auto bar(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
#else
constexpr auto v = S::s_v; // OK for clang and gcc
#endif
return v;
}
int main() {}
Live Example compiled with -std=c++1z and #define TEST 1 for Clang 5.0 SVN, with error message:
Start
prog.cc:12:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
^~~~~
prog.cc:22:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
^~~~~
2 errors generated.
1
Finish
Question: is this is a Clang bug, or is gcc too liberal in accepting both syntax forms for static member access in a constexpr function?
Clang seems to be in the right. When accessing a static member with the member access syntax [class.static/1]:
A static member s of class X may be referred to using the qualified-id
expression X​::​s; it is not necessary to use the class member access
syntax to refer to a static member. A static member may be referred to
using the class member access syntax, in which case the object
expression is evaluated.
So s.v() will cause s to be evaluated. Now, according to [expr.const/2.11], s is not a constant expression:
2 An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference
type unless the reference has a preceding initialization and either:
(2.11.1) - it is initialized with a constant expression or
(2.11.2) - its lifetime began within the evaluation of e;
s doesn't have a preceding initialization with a constant expression, not in the scope of foo.
If you want to access the static members based of a function parameter, without hard-coding the type, the way forward is std::remove_reference_t<decltype(s)>. This is accepted by Clang and GCC both:
#include <type_traits>
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
constexpr auto foo(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::v();
return v;
}
constexpr auto bar(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::s_v;
return v;
}
int main() {}
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
I guess it depends on whether you compile in C++11 or C++14 mode. If you look over at cppreference, you will find (emphasis added by me):
A core constant expression is any expression that does not have any one of the following
(...)
6) The this pointer, except if used for class member access inside a non-static member function (until C++14)
6) The this pointer, except in a constexpr function or a constexpr constructor that is being evaluated as part of the expression (since C++14)
So, in C++11, whatever happens inside s.v() would not be considered a constant expression, since it uses the this pointer, but it is not a non-static member function (it's static) accessing a class member.
Per C++14, however, it would be, since it is evaluating a constexpr function as part of the expression, so the "except if" clause on the "does not have any of" set of rules catches.
Now don't ask me whether that makes any sense or whether anyone is supposed to understand that... :-)

C++ constexpr count constructor calls

I'm trying to static instantiate some objects at compile time. What I need is to set to a member int variable an incremented value. For example, the first object I create will have the 0 value, the second 1, the third 2...
Summarizing I need something like this but it has to work as constexpr.
//header
class MyClass final {
private:
static int IDcount;
public:
const int ID;
constexpr MyClass(args);
//~MyClass();
};
//source
int MyClass::IDcount = 0;
constexpr MyClass::MyClass(args) : ID(MyClass::IDcount++) {
}
Is there a way to achieve this at compile time (without taking the ID as argument of the constructor)
It can't be done the way you've defined it, but there is a non-standard but widely implemented preprocessor trick that could be employed.
#include <iostream>
struct MyClass final {
constexpr MyClass(int myid, const char *myname)
: id(myid), name(myname) {}
int id;
const char *name;
};
constexpr MyClass m[]{
MyClass(__COUNTER__, "Larry"),
MyClass(__COUNTER__, "Moe"),
MyClass(__COUNTER__, "Curly")
};
int main()
{
for (auto const &obj : m)
std::cout << obj.id << '\t' << obj.name << "\n";
}
The __COUNTER__ macro is defined in Microsoft's Visual C++, gcc since version 4.3 and clang.
When run, this program produces:
0 Larry
1 Moe
2 Curly
A constexpr-function or ctor must be a valid core constant expression on at least one path:
7.1.5 The constexpr specifier [dcl.constexpr]
5 For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
Which means it cannot modify a global object on all paths:
5.19 Constant expressions [expr.const]
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type
that refers to a non-volatile object whose lifetime began within the evaluation of e;