I am having trouble understanding why both gcc-8.2.0 and clang-7.0.0 reject the following code (live code here):
#include <array>
int main() {
constexpr std::array<int,3> v{1,2,3};
constexpr auto b = v.begin(); // error: not a constexpr
return 0;
}
with the error
error: '(std::array<int, 3>::const_pointer)(& v.std::array<int,3>::_M_elems)'
is not a constant expression (constexpr auto b = v.begin();)
According to en.cppreference.com, the begin() member function is declared constexpr. Is this a compiler bug?
So let's sidestep std::array to make this a bit easier:
template <typename T, size_t N>
struct array {
T elems[N];
constexpr T const* begin() const { return elems; }
};
void foo() {
constexpr array<int,3> v{{1, 2, 3}};
constexpr auto b = v.begin(); // error
}
constexpr array<int, 3> global_v{{1, 2, 3}};
constexpr auto global_b = global_v.begin(); // ok
Why is b an error but global_b is okay? Likewise, why would b become okay if we declared v to be static constexpr? The problem is fundamentally about pointers. In order to have a constant expression that's a pointer, it has to point to one, known, constant thing, always. That doesn't really work for local variables without static storage duration, since they have fundamentally mutable address. But for function-local statics or globals, they do have one constant address, so you can take a constant pointer to them.
In standardese, from [expr.const]/6:
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, [...]
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 function, or a null pointer value, and
[...]
b is none of those things in the second bullet, so this fails. But global_b satisfies the bolded condition - as would b if v were declared static.
Related
I've tried this with VC++, gcc and clang and I get slightly different results for all of them.
VC++ says it's ok.
if the constexpr object is not declared static, then both gcc and clang say something similar:
gcc Demo:
<source>: In function 'int main()':
<source>:141:30: error: 'ArrayStack<int, 2>{int [2]{2, 1}, (((int*)(& y.ArrayStack<int, 2>::m_stack)) + 8)}' is not a constant expression
141 | constexpr auto y { fn2() };
| ^
<source>:142:29: error: non-constant condition for static assertion
142 | static_assert( y.back() == 1, "Should equal 1.");
| ~~~~~~~~~^~~~
<source>:143:44: error: 'ArrayStack<int, 2>{int [2]{2, 1}, (((int*)(& z.ArrayStack<int, 2>::m_stack)) + 8)}' is not a constant expression
143 | constexpr ArrayStack<int, 2> z { fn2() };
| ^
<source>:144:29: error: non-constant condition for static assertion
144 | static_assert( z.back() == 1, "Should equal 1.");
| ~~~~~~~~~^~~~
clang Demo:
<source>:141:20: error: constexpr variable 'y' must be initialized by a constant expression
constexpr auto y { fn2() };
^~~~~~~~~~~
<source>:141:20: note: pointer to subobject of 'y' is not a constant expression
<source>:141:20: note: address of non-static constexpr variable 'y' may differ on each invocation of the enclosing function; add 'static' to give it a constant address
constexpr auto y { fn2() };
^
static
<source>:142:20: error: static assertion expression is not an integral constant expression
static_assert( y.back() == 1, "Should equal 1.");
^~~~~~~~~~~~~
<source>:142:22: note: initializer of 'y' is not a constant expression
static_assert( y.back() == 1, "Should equal 1.");
^
<source>:141:20: note: declared here
constexpr auto y { fn2() };
^
Since clang gave a suggestion of using the static keyword, I tried that and got these errors:
gcc Demo:
Accepted the assignment of the constexpr object to a constexpr variable, BUT didn't allow using a constexpr member function in a constexpr context (static_assert).
<source>:142:29: error: the value of 'y' is not usable in a constant expression
<source>:141:27: note: 'y' used in its own initializer
141 | static constexpr auto y { fn2() };
| ^
<source>:144:29: error: non-constant condition for static assertion
144 | static_assert( z.back() == 1, "Should equal 1.");
| ~~~~~~~~~^~~~
clang Demo:
Accepted both assignment and usage in a constexpr context.
MSVC++ Demo:
Didn't care if was static or not.
Here is the relevant code (full code can be seen in the Demo links above):
template <typename Type, size_t Size>
class ArrayStack {
public:
using iterator = Type*;
using const_iterator = Type const*;
using value_type = Type;
using pointer = Type*;
using reference = Type&;
using difference_type = std::make_signed_t<size_t>;
using size_type = size_t;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr ArrayStack()
: m_stack{}
, m_end{ begin() }
{}
// Init only used part of m_stack is only available in C++20
constexpr ArrayStack(ArrayStack const& to_copy)
: m_stack{}
, m_end{ begin() }
{
*this = to_copy;
}
constexpr ArrayStack& operator=(ArrayStack const& rhs) {
// std::copy is constexpr in C++20
auto it{ begin() };
for (auto rhs_it{ rhs.begin() }, rhs_end_it{ rhs.end() };
rhs_it != rhs_end_it;
++it, ++rhs_it)
{
*it = *rhs_it;
}
m_end = begin() + rhs.size();
return *this;
}
//...
constexpr iterator begin() { return m_stack; }
constexpr const_iterator begin() const { return m_stack; }
//...
constexpr iterator end() { return m_end; }
constexpr const_iterator end() const { return m_end; }
//...
constexpr void push_back(value_type const& value) {
if (!full()) {
m_end++[0] = value;
}
else {
throw std::out_of_range("Ran out of stack space");
}
}
//...
constexpr size_t size() const {
return end() - begin();
}
constexpr value_type const& back() const {
if (!empty()) {
return m_end[-1];
}
else {
throw std::out_of_range("Nothing on stack");
}
}
//...
constexpr bool empty() const {
return m_end == begin();
}
constexpr bool full() const {
return end() == m_stack + Size;
}
private:
Type m_stack[Size];
iterator m_end;
};
constexpr bool fn() {
ArrayStack<int, 2> stack;
stack.push_back(2);
stack.push_back(1);
return stack.back() == 1;
}
constexpr auto fn2() {
ArrayStack<int, 2> stack;
stack.push_back(2);
stack.push_back(1);
return stack;
}
int main() {
static_assert( sizeof(int) == 4, "Should be 4 bytes in size");
static_assert( fn(), "Should return true.");
static_assert( fn2().back() == 1, "Should equal 1.");
constexpr auto y { fn2() };
static_assert( y.back() == 1, "Should equal 1.");
constexpr ArrayStack<int, 2> z { fn2() };
static_assert( z.back() == 1, "Should equal 1.");
return 0;
}
This appears to be caused by the pointer in the object. Replacing the pointer with an index to one past the end seems to fix the issue, though the data layout is a bit odd as it seems to have values which shouldn't really contribute to anything as the result should be compile time data (with directives showing) and there is a lot more data than there should be, even at -O3. Also, the compile time data isn't used, so it should also be just thrown away.
gcc (with directives showing, using -O3) left is without static Demo, right is with static Demo:
...
clang had way too many differences to be able to analyze effectively. They appeared to be byte value changes, label changes and possibly ordering changes.
MSVC++ (show directives doesn't seem to change anything, using -O3). Left is without static, right is with static.
Note that without the directives showing with -O3, the main() was the same for gcc and clang (with directives showing and using -O3) :
main:
xor eax, eax
ret
Question is why is the pointer causing such havoc? Am I correct in assuming that this behaviour is a bug on the gcc/clang's part?
Side question: Why is a static constexpr variable resulting in different code than a constexpr variable? As they are both generate compile time constants, they should result in the same code, shouldn't they?
Note:
In my index version, there is a small bug where m_iend isn't assigned to rhs.m_iend at the end of operator=(). This doesn't affect the code path depicted, but it is an error if the assignment is done outside of the copy constructor.
MSVC is pretty clearly in error for accepting your code (maybe it's just trying to be "nice"). Clang's error message is to the point: y would contain a pointer (m_end) to some part of itself. Pointers are not allowed in the results of constant expressions unless they refer to static objects. A constexpr variable must be initialized from a constant expression. So y needs to be static to be constexpr.
[expr.const] (as of C++20, but much the same in C++14)
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.
Clang is also giving the rationale for why C++ is defined this way: the value of a pointer is an address to an object. If said pointer is supposed to always have the same value (address) no matter when you evaluate it (which is what constexpr means), the object it is referring to must be static. I'm not sure what you're getting at with the assembly: if y, z are static constexpr then Clang makes main a no-op. What more could you want?
That GCC chokes on the static_asserts (but not on the static constexpr auto y{fn2()};!) does seem to me to be a GCC bug.
Having internal pointers in objects is annoying and error-prone anyway. Use an index.
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... :-)
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.
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... :-)
In C++ Primer, Fifth Edition, §6.5.2:
A constexpr function is defined like any other function but must meet certain restrictions: The return type and the type of each parameter in must be a literal type (§2.4.4, p. 66), and the function body must contain exactly one return statement
but another sentence in this chapter (page 239):
A constexpr function is permitted to return a value that is not a constant
// scale(arg) is a constant expression if arg is a constant expression
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
Is it a contradictory summary? I am confused about it.
The return type of scale is literal type?
update:
what's the difference between literal type and constant ?
First of all what I believe the author meant was that a constexpr function does not have to result in a constant expression, which is an expression that can be evaluated at compile time.
A constexpr function will only yield a constant expression if the arguments to the function are also constant expressions and the comment right after says exactly that:
// scale(arg) is a constant expression if arg is a constant expression
and the examples that follow right after also demonstrate this behavior:
int arr[scale(2)]; // ok: scale(2) is a constant expression
int i = 2; // i is not a constant expression
int a2[scale(i)]; // error: scale(i) is not a constant expression
In C++(as opposed to C99) as array size must be a constant expression and so the last case is an error since the argument to scale is not a constant expression.
This is different concept from the return type of the function, which must be a literal type which is any of the following:
void(since c++14) (so that constexpr functions can return void)
scalar type which includes Arithmetic types, enumeration types, pointer types, pointer to member types, std::nullptr_-
t, and cv-qualified versions of these types)
reference type
an array of literal type
class type that has all of the following properties:
has a trivial destructor,
is either
an aggregate type
a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor
all non-static data members and base classes are of non-volatile literal types.
It's not contradictory. As well as mandating that the return type must be of "literal type", the draft standard states that a call to a constexpr function does not have to appear in a constant expression. From the C++11 draft standard:
§7.1.5/7 A call to a constexpr function produces the same result as
a call to a equivalent non-constexpr function in all respects
except that a call to a constexpr function can appear in a constant
expression.
constexpr does nothing but tells the compiler that the value is there in compile time, so you can use it as template argument (for example)
int a1 = 5;
std::array<int, a1> arr1; // error, a is variable
const int a2 = 5;
std::array<int, a2> arr2; // OK
int f1() { return 3; }
std::array<int, f1()> arr3; // error, compiler doesn't know it is const 3
constexpr int f2() { return 3; }
std::array<int, f2()> arr4; // OK
Later you also can:
constexpr int f3() { return f1() + 1; } // error, f1 is not constexpr
constexpr int f4() { return f2() + 1; } // OK
std::array<int, f4()> arr5; // OK
Now about literal types limitation: the function arguments and result types should be literal types (Need clarification on definition of literal type), the very same limitation as template arguments apply (known in compile types).
constexpr std::string f5() { return "hello"; } // error,
// std::string is not literal type
constexpr const std::string& f6() {
static const std::string s = "hello";
return s;
}
template<const std::string& s> SomeClass { ... };
SomeClass<f6()> someObject;