Why doesn't the following minimal example compile with c++11 nor c++14, but compiles in c++17 and c++2a?
#include <iostream>
#include <limits>
#include <vector>
// works:
// static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
class Classy {
// does not work in c++11 (constexpr introduced) nor c++14:
// works if c++17 or newer:
static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
int VALUE_LIMIT_B = std::numeric_limits<int>::max();
public:
explicit Classy();
std::vector<int> classy;
};
Classy::Classy() {
// does not work:
classy.resize(3, VALUE_LIMIT_A);
// works:
// classy.resize(3, std::numeric_limits<int>::max());
// works:
// std::cout << VALUE_LIMIT_A;
// works:
// classy.resize(3, VALUE_LIMIT_B);
}
// required in c++11 and c++14
// constexpr int Classy::VALUE_LIMIT_A;
int main() {
Classy classy{};
for (const auto& elem : classy.classy) {
std::cout << elem << ",";
}
std::cout << "\n";
}
Here is output with c++11:
$ g++ -std=c++11 main.cpp && ./a.out
/tmp/ccon7pPo.o: In function `Classy::Classy()':
main.cpp:(.text+0x31): undefined reference to `Classy::VALUE_LIMIT_A'
collect2: error: ld returned 1 exit status
Here is output with c++17:
$ g++ -std=c++17 main.cpp && ./a.out
2147483647,2147483647,2147483647,
Because since C++17, the definition of the constexpr static data member at namespace scope is not required again.
If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition
at namespace scope is still required, but it cannot have an
initializer. This definition is deprecated for constexpr data members (since C++17).
struct X {
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used
const int X::n; // … so a definition is necessary
constexpr int X::m; // … (except for X::m in C++17)
If a static data member is declared constexpr, it is implicitly inline
and does not need to be redeclared at namespace scope. This
redeclaration without an initializer (formerly required as shown
above) is still permitted, but is deprecated. (since C++17)
And note that std::vector::resize takes the 2nd parameter by reference; which cause VALUE_LIMIT_A to be odr-used for classy.resize(3, VALUE_LIMIT_A);.
Since C++17, with the introduction of inline variables, static constexpr data members are implicitly inline variables:
[dcl.constexpr]
1 ... A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable
([dcl.inline])...
Inline variables, like inline functions, are defined in every translation units they are used in. And the compiler resolves the multiple definitions into a single one. This means, the unlike C++14, there is no need to explicitly provide an out of class definition for a static constexpr variable for the sake of the ODR, the compiler takes care of it.
You can also get away with it in C++14, too. Like the other answer mentions, it is resize that ODR-uses the static data member. You can work around it, though:
classy.resize(3, int(VALUE_LIMIT_A));
While it looks superfluous, it in fact has different behavior to using the constant directly. This creates an integer temporary, with the value of the constant. But it doesn't ODR-use the constant. The temporary is bound to the reference instead, and so the problem is avoided. While it's better to define the constant in pre-C++17 code, you can use this trick to adapt code you have no control over.
Related
consider a class with a constant member:
class foo {
public:
const static int N;
};
foo::N needs to initialized:
constexpr int foo::N = 5;
and note how using the constexpr qualifier instead of const here seems like a mistake.
but GCC, Clang and MSVC all compile just fine!
is there something in the language that allows changing qualifiers here?
is it an error overlooked by all three compilers?
Clang even allows both qualifier versions simultaneously:
constexpr int foo::N = 3;
const int foo::N = 5;
int main(){
return foo::N; //returns 3
}
what's going on?
Since obviously the value of a variable that has been declared but not defined cannot be used in a constant expression, constexpr pertains only to the definition of a variable. (Of course, if the variable is inline, perhaps because of being a static member declared constexpr, every definition must have constexpr.) It implies const (on the variable itself: constexpr char* is char *const, not const char*), so you haven’t changed the variable’s type. This is no different from
// foo.hpp
extern const int x;
// foo.cpp
constexpr int x=2;
which is perhaps less surprising.
So everything is fine! The variable’s value can be used in constant expressions only in the translation unit that contains the definition, but that’s no surprise, and could easily be construed as a feature for modularity. Clang is buggy to allow two definitions: presumably it’s trying to ignore the (deprecated, as of C++17) out-of-class definition for a constexpr static data member defined in the class.
Why does emplace_back take a reference of the member that requires a definition? What is the difference between emplace_back(integer literal) and emplace_back(static constexpr integer member)?
If I switch to C++17, it compiles fine. I found that in C++17 static constexpr data members are implicitly inlined. Does it mean the compiler implicitly creates a definition for them?
Example code:
class base {
int n;
public:
base(int n):n(n) {}
};
struct base_trait {
static constexpr int n = 1;
};
int main(void) {
vector<base> v;
v.emplace_back(1); // ok
v.emplace_back(base_trait::n); // link error with -std=c++14, ok with -std=c++17
return 0;
}
As you said, emplace_back takes arguments by reference, so passing base_trait::n causes it to be odr-used.
an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it;
Before C++17, that means the definiton of base_trait::n is required here. But since C++17 the behavior changed, for constexpr static data member the out-of-class definition is not needed again.
If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for constexpr data members (since C++17).
A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition. (since C++17)
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... :-)
Requirements
I want a constexpr value (i.e. a compile-time constant) computed from a constexpr function. And I want both of these scoped to the namespace of a class, i.e. a static method and a static member of the class.
First attempt
I first wrote this the (to me) obvious way:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x says to that:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x complains:
error: field initializer is not constant
Second attempt
OK, I thought, perhaps I have to move things out of the class body. So I tried the following:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3 will compile that without complaints. Unfortunately, my other code uses some range-based for loops, so I have to have at least 4.6. Now that I look closer at the support list, it appears that constexpr would require 4.6 as well. And with g++-4.6.3 I get
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
This sounds really strange to me. How do things “differ in constexpr” here? I don't feel like adding -fpermissive as I prefer my other code to be rigurously checked. Moving the foo implementation outside the class body had no visible effect.
Expected answers
Can someone explain what is going on here? How can I achieve what I'm attempting to do? I'm mainly interested in answers of the following kinds:
A way to make this work in gcc-4.6
An observation that later gcc versions can deal with one of the versions correctly
A pointer to the spec according to which at least one of my constructs should work, so that I can bug the gcc developers about actually getting it to work
Information that what I want is impossible according to the specs, preferrably with some insigt as to the rationale behind this restriction
Other useful answers are welcome as well, but perhaps won't be accepted as easily.
The Standard requires (section 9.4.2):
A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer.
Your first code is correct. It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (e.g. ideone.com is still stuck on gcc 4.5).
This isn't possible, because unfortunately the Standard precludes initializing a static constexpr data member in any context where the class is complete. The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static.
The most likely reason for this is that constexpr variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:
an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;
Consider:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard:
9.4.2 [class.static.data] p3: ... A static data member of literal type can be declared in the class definition with the constexpr specifier;
if so, its declaration shall specify a brace-or-equal-initializer in
which every initializer-clause that is an assignment-expression is a
constant expression.
2) The code in MvG's question:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing).
Some facts:
I do agree though that class C1 is not complete at the point of invocation of foo (based on 9.2p2) but completeness or incompleteness of the class C1 says nothing about whether foo is defined as far as the standard is concerned.
I did search the standard for the definedness of member functions but didn't find anything.
So the statement mentioned by Ben doesn't apply here if my logic is valid:
an invocation of an undefined constexpr function or an undefined
constexpr constructor outside the definition of a constexpr function
or a constexpr constructor;
3) The last example given by Ben, simplified:
class C1
{
constexpr static int foo() { return bar; }
constexpr static int bar = foo();
};
looks invalid but for different reasons and not simply because foo is called in the initializer of bar. The logic goes as follows:
foo() is called in the initializer of the static constexpr member bar, so it has to be a constant expression (by 9.4.2 p3).
since it's an invocation of a constexpr function, the Function invocation substitution (7.1.5 p5) kicks in.
Their are no parameters to the function, so what's left is "implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization." (7.1.5 p5)
the return expression is just bar, which is a lvalue and the lvalue-to-rvalue conversion is needed.
but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:
an lvalue-to-rvalue conversion (4.1) unless it is applied to:
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.
hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).
so by bullet 4 in (5.19 p2), the call to foo() is not a constant expression:
an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
Such initialization works well but only on clang
Probably, the problem here is related to the order of declaration/definitions in a class. As you all know, you can use any member even before it is declared/defined in a class.
When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.
Perhaps, Philip answer, related to this idea, is a good point to understand the question.
Note this code which compiles without problems:
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);
I have some linking issues which I do not understand.
Here are the source:
#include <iostream>
template < typename T >
struct CompressedEnums {
CompressedEnums () : data(0) {}
T get() const {
return (T)(data);
}
void set(const T& value) {
data = value;
}
unsigned data;
};
namespace Bbs_detail {
enum inner_type { BB0 = 0 , BB1 = 1 , BB2 = 2 };
typedef inner_type E;
};
struct Bbs {
static const size_t size = 3;
typedef Bbs_detail::inner_type inner_type;
typedef inner_type E;
static const Bbs_detail::E BB0 = Bbs_detail::BB0;
static const Bbs_detail::E BB1 = Bbs_detail::BB1;
static const Bbs_detail::E BB2 = Bbs_detail::BB2;
};
std::ostream& operator<<(std::ostream& o, const Bbs::E& e) {
switch(e) {
case Bbs::BB0: o << "BB0"; return o;
case Bbs::BB1: o << "BB1"; return o;
case Bbs::BB2: o << "BB2"; return o;
}
return o;
};
int main(int argc, const char *argv[]) {
CompressedEnums< Bbs::E > l;
l.set(Bbs::BB0);
Bbs::E x = l.get();
std::cout << x << std::endl;
return 0;
}
When I compile this with -O3 it works, but I get linker errors with -O0. Ih have tried both with gcc 4.6.2 and and gcc 4.7.
When compiling with clang 3.0 I get linker errors regardless of the optimization level.
Linker errors:
/tmp/cch116DO.o: In function `main':
test.cxx:(.text+0x8f): undefined reference to `Bbs::BB0'
collect2: error: ld returned 1 exit status
Is this because I'm doing something illegal?
I was of the opinion that static const members of integral types can be initialized in-class, isn't that right?
Citing the C++98 standard, 9.4.2 Static data members:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant. The member shall still be defined in a namespace scope if it is used in the program and the namespace
scope definition shall not contain an initializer. [emphasis mine].
So you have to define the constant members is Alan Stokes pointed in his answer. If you don't do that, and the compiler manages to avoid any reference to the variable, because it is constant and it already knows the value, you may do without it. But no guarantees about that.
Curiously enough in the C++11 draft, there are a few additional notes about constexpr and brace initializers, and then:
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program.
Then, in point 3.2 it defines what odr-used means:
A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.
That is, in C++11 you have the guarantee not to need the member definition if all your uses of the constant member are in constant expressions:
If you declare a static member of a class, you also need to define it in exactly one source file.
(Often you can get away without doing this, but not always, and the standard requires it.)
So somewhere you need
const Bbs_detail::E Bbs::BB0;
etc.