I was working on a C++11 project solely using clang++-3.4, and decided to compile using g++-4.8.2 in case there were any discrepancies in the errors produced. It turned out that g++ rejects some code that clang++ accepts. I have reduced the problem to the MWE given below.
enum { a };
template <class T>
struct foo
{
static constexpr auto value = a;
};
int main()
{
static constexpr auto r = foo<int>::value;
}
foo.cpp:5:23: error: ‘const<anonymous enum> foo<int>::value’, declared using anonymous type, is used but never defined [-fpermissive]
static const auto value = A;
I would like some help answering the following two questions:
Which compiler is correct in its interpretation of the standard? I am assuming that one compiler is right in either accepting or rejecting the code, and the other is wrong.
How can I work around this issue? I can't name the anonymous enum, because it is from a third-party library (in my case, the enums were Eigen::RowMajor and Eigen::ColMajor).
Who's to blame?
GCC is inaccurately rejecting your snippet, it is legal according to the C++11 Standard (N3337). Quotations with proof and explanation is located the end of this post.
workaround (A) - add the missing definition
template <class T>
struct foo {
static constexpr auto value = a;
typedef decltype(a) value_type;
};
template<class T>
constexpr typename foo<T>::value_type foo<T>::value;
workaround (B) - use the underlying-type of the enumeration as placeholder
#include <type_traits>
template <class T>
struct foo {
static const std::underlying_type<decltype(a)>::type value = a;
};
What does the Standard say? (N3337)
As stated, the snippet is legal C++11, as can be read in the following quoted sections.
When can we use a type without linkage?
[basic.link]p8 has detailed wording that describes when a type is "without linkage", and it states that an unnamed enumeration count as such type.
[basic.link]p8 also explicitly states three contexts where such a type cannot be used, but not one of the contexts apply to our usage, so we are safe.
A type without linkage shall not be used as the type of a variable or function with external linkage unless
the entity has C language linkage (7.5), or
the entity is declared within an unnamed namespace (7.3.1), or
the entity is not odr-used (3.2) or is defined in the same translation unit
Are you sure we can use auto in such context?
Yes, and this can be proven by the following quote:
7.1.6.4p auto specifier [dcl.spec.auto]
A auto type-specifier can also be used in declaring a variable in the condition of a selection statement (6.4) or an iteration statement (6.5), in the type-specifier-seq in the new-type-id or type-id of a new-expression (5.3.4), in a for-range-declaration, and in declaring a static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition (9.4.2).
Which compiler is correct in its interpretation of the standard?
gcc is incorrect. §9.4.2/3:
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. The member
shall still be defined in a namespace scope if it is odr-used (3.2) in
the program and the namespace scope definition shall not contain an
initializer.
And the name is not odr-used as per §3.2:
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.
This is indeed the case: It does satisfy the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied (it is used as an initializer for an object). So GCC's rejection is incorrect.
A possible workaround is to define the member (but without a placeholder type). This definition is sufficient for both Clang and GCC:
template< typename T >
constexpr decltype(a) foo<T>::value;
Workaround with decltype:
enum { a };
template <class T>
struct foo
{
static constexpr auto value = a;
};
template <class T>
constexpr decltype(a) foo<T>::value;
int main()
{
static constexpr auto r = foo<int>::value;
}
Related
I want to have a static const char array in my class. GCC complained and told me I should use constexpr, although now it's telling me it's an undefined reference. If I make the array a non-member then it compiles. What is going on?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
Add to your cpp file:
constexpr char foo::baz[];
Reason: You have to provide the definition of the static member as well as the declaration. The declaration and the initializer go inside the class definition, but the member definition has to be separate.
C++17 introduces inline variables
C++17 fixes this problem for constexpr static member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.
Proposal P0386 Inline Variables introduces the ability to apply the inline specifier to variables. In particular to this case constexpr implies inline for static member variables. The proposal says:
The inline specifier can be applied to variables as well as to functions. A variable declared
inline has the same semantics as a function declared inline: it can be defined, identically, in
multiple translation units, must be defined in every translation unit in which it is odr-used, and
the behavior of the program is as if there is exactly one variable.
and modified [basic.def]p2:
A declaration is a definition unless
...
it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),
...
and add [depr.static_constexpr]:
For compatibility with prior C++ International Standards, a constexpr
static data member may be redundantly redeclared outside the class
with no initializer. This usage is deprecated. [ Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
C++14 and earlier
In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr this was extended to literal types.
In C++11, we do not need to provide a namespace scope definition for a static constexpr member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2 [class.static.data] which says (emphasis mine going forward):
[...]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. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ]
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition
shall not contain an initializer.
So then the question becomes, is baz odr-used here:
std::string str(baz);
and the answer is yes, and so we require a namespace scope definition as well.
So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. 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.
So baz does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz being an array. This is covered in section 4.1 [conv.lval] which says :
A glvalue (3.10) of a non-function, non-array type T can be
converted to a prvalue.53 [...]
What is applied in the array-to-pointer conversion.
This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.
This is really a flaw in C++11 - as others have explained, in C++11 a static constexpr member variable, unlike every other kind of constexpr global variable, has external linkage, thus must be explicitly defined somewhere.
It's also worth noting that you can often in practice get away with static constexpr member variables without definitions when compiling with optimization, since they can end up inlined in all uses, but if you compile without optimization often your program will fail to link. This makes this a very common hidden trap - your program compiles fine with optimization, but as soon as you turn off optimization (perhaps for debugging), it fails to link.
Good news though - this flaw is fixed in C++17! The approach is a bit convoluted though: in C++17, static constexpr member variables are implicitly inline. Having inline applied to variables is a new concept in C++17, but it effectively means that they do not need an explicit definition anywhere.
My workaround for the external linkage of static members is to use constexpr reference member getters (which doesn't run into the problem #gnzlbg raised as a comment to the answer from #deddebme).
This idiom is important to me because I loathe having multiple .cpp files in my projects, and try to limit the number to one, which consists of nothing but #includes and a main() function.
// foo.hpp
struct foo {
static constexpr auto& baz() { return "quz"; }
};
// some.cpp
auto sz = sizeof(foo::baz()); // sz == 4
auto& foo_baz = foo::baz(); // note auto& not auto
auto sz2 = sizeof(foo_baz); // 4
auto name = typeid(foo_baz).name(); // something like 'char const[4]'
Isn't the more elegant solution be changing the char[] into:
static constexpr char * baz = "quz";
This way we can have the definition/declaration/initializer in 1 line of code.
On my environment, gcc vesion is 5.4.0. Adding "-O2" can fix this compilation error. It seems gcc can handle this case when asking for optimization.
I want to have a static const char array in my class. GCC complained and told me I should use constexpr, although now it's telling me it's an undefined reference. If I make the array a non-member then it compiles. What is going on?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
Add to your cpp file:
constexpr char foo::baz[];
Reason: You have to provide the definition of the static member as well as the declaration. The declaration and the initializer go inside the class definition, but the member definition has to be separate.
C++17 introduces inline variables
C++17 fixes this problem for constexpr static member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.
Proposal P0386 Inline Variables introduces the ability to apply the inline specifier to variables. In particular to this case constexpr implies inline for static member variables. The proposal says:
The inline specifier can be applied to variables as well as to functions. A variable declared
inline has the same semantics as a function declared inline: it can be defined, identically, in
multiple translation units, must be defined in every translation unit in which it is odr-used, and
the behavior of the program is as if there is exactly one variable.
and modified [basic.def]p2:
A declaration is a definition unless
...
it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),
...
and add [depr.static_constexpr]:
For compatibility with prior C++ International Standards, a constexpr
static data member may be redundantly redeclared outside the class
with no initializer. This usage is deprecated. [ Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
C++14 and earlier
In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr this was extended to literal types.
In C++11, we do not need to provide a namespace scope definition for a static constexpr member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2 [class.static.data] which says (emphasis mine going forward):
[...]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. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ]
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition
shall not contain an initializer.
So then the question becomes, is baz odr-used here:
std::string str(baz);
and the answer is yes, and so we require a namespace scope definition as well.
So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. 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.
So baz does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz being an array. This is covered in section 4.1 [conv.lval] which says :
A glvalue (3.10) of a non-function, non-array type T can be
converted to a prvalue.53 [...]
What is applied in the array-to-pointer conversion.
This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.
This is really a flaw in C++11 - as others have explained, in C++11 a static constexpr member variable, unlike every other kind of constexpr global variable, has external linkage, thus must be explicitly defined somewhere.
It's also worth noting that you can often in practice get away with static constexpr member variables without definitions when compiling with optimization, since they can end up inlined in all uses, but if you compile without optimization often your program will fail to link. This makes this a very common hidden trap - your program compiles fine with optimization, but as soon as you turn off optimization (perhaps for debugging), it fails to link.
Good news though - this flaw is fixed in C++17! The approach is a bit convoluted though: in C++17, static constexpr member variables are implicitly inline. Having inline applied to variables is a new concept in C++17, but it effectively means that they do not need an explicit definition anywhere.
My workaround for the external linkage of static members is to use constexpr reference member getters (which doesn't run into the problem #gnzlbg raised as a comment to the answer from #deddebme).
This idiom is important to me because I loathe having multiple .cpp files in my projects, and try to limit the number to one, which consists of nothing but #includes and a main() function.
// foo.hpp
struct foo {
static constexpr auto& baz() { return "quz"; }
};
// some.cpp
auto sz = sizeof(foo::baz()); // sz == 4
auto& foo_baz = foo::baz(); // note auto& not auto
auto sz2 = sizeof(foo_baz); // 4
auto name = typeid(foo_baz).name(); // something like 'char const[4]'
Isn't the more elegant solution be changing the char[] into:
static constexpr char * baz = "quz";
This way we can have the definition/declaration/initializer in 1 line of code.
On my environment, gcc vesion is 5.4.0. Adding "-O2" can fix this compilation error. It seems gcc can handle this case when asking for optimization.
I want to have a static const char array in my class. GCC complained and told me I should use constexpr, although now it's telling me it's an undefined reference. If I make the array a non-member then it compiles. What is going on?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
Add to your cpp file:
constexpr char foo::baz[];
Reason: You have to provide the definition of the static member as well as the declaration. The declaration and the initializer go inside the class definition, but the member definition has to be separate.
C++17 introduces inline variables
C++17 fixes this problem for constexpr static member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.
Proposal P0386 Inline Variables introduces the ability to apply the inline specifier to variables. In particular to this case constexpr implies inline for static member variables. The proposal says:
The inline specifier can be applied to variables as well as to functions. A variable declared
inline has the same semantics as a function declared inline: it can be defined, identically, in
multiple translation units, must be defined in every translation unit in which it is odr-used, and
the behavior of the program is as if there is exactly one variable.
and modified [basic.def]p2:
A declaration is a definition unless
...
it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),
...
and add [depr.static_constexpr]:
For compatibility with prior C++ International Standards, a constexpr
static data member may be redundantly redeclared outside the class
with no initializer. This usage is deprecated. [ Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
C++14 and earlier
In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr this was extended to literal types.
In C++11, we do not need to provide a namespace scope definition for a static constexpr member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2 [class.static.data] which says (emphasis mine going forward):
[...]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. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ]
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition
shall not contain an initializer.
So then the question becomes, is baz odr-used here:
std::string str(baz);
and the answer is yes, and so we require a namespace scope definition as well.
So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. 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.
So baz does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz being an array. This is covered in section 4.1 [conv.lval] which says :
A glvalue (3.10) of a non-function, non-array type T can be
converted to a prvalue.53 [...]
What is applied in the array-to-pointer conversion.
This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.
This is really a flaw in C++11 - as others have explained, in C++11 a static constexpr member variable, unlike every other kind of constexpr global variable, has external linkage, thus must be explicitly defined somewhere.
It's also worth noting that you can often in practice get away with static constexpr member variables without definitions when compiling with optimization, since they can end up inlined in all uses, but if you compile without optimization often your program will fail to link. This makes this a very common hidden trap - your program compiles fine with optimization, but as soon as you turn off optimization (perhaps for debugging), it fails to link.
Good news though - this flaw is fixed in C++17! The approach is a bit convoluted though: in C++17, static constexpr member variables are implicitly inline. Having inline applied to variables is a new concept in C++17, but it effectively means that they do not need an explicit definition anywhere.
My workaround for the external linkage of static members is to use constexpr reference member getters (which doesn't run into the problem #gnzlbg raised as a comment to the answer from #deddebme).
This idiom is important to me because I loathe having multiple .cpp files in my projects, and try to limit the number to one, which consists of nothing but #includes and a main() function.
// foo.hpp
struct foo {
static constexpr auto& baz() { return "quz"; }
};
// some.cpp
auto sz = sizeof(foo::baz()); // sz == 4
auto& foo_baz = foo::baz(); // note auto& not auto
auto sz2 = sizeof(foo_baz); // 4
auto name = typeid(foo_baz).name(); // something like 'char const[4]'
Isn't the more elegant solution be changing the char[] into:
static constexpr char * baz = "quz";
This way we can have the definition/declaration/initializer in 1 line of code.
On my environment, gcc vesion is 5.4.0. Adding "-O2" can fix this compilation error. It seems gcc can handle this case when asking for optimization.
The following C++11 program produces an undefined reference to S<42>::s error in both gcc and clang...
#include <iostream>
template<int i> struct S;
template<> struct S<42> { static constexpr char s[] = "foo"; };
int main()
{
std::cout << S<42>::s << std::endl;
}
By adding the following extra declaration (related question) to the program...
constexpr char S<42>::s[];
...it suppresses the error, and the program compiles.
Where in the standard does it specify that a program without that extra declaration is ill-formed? Which rules are in play here exactly?
§ 9.4.2/3:
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. The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
[emphasis added]
It looks to me like when you print out the value in main that's an odr-use, so the compilers are right: the definition is required.
I want to have a static const char array in my class. GCC complained and told me I should use constexpr, although now it's telling me it's an undefined reference. If I make the array a non-member then it compiles. What is going on?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
Add to your cpp file:
constexpr char foo::baz[];
Reason: You have to provide the definition of the static member as well as the declaration. The declaration and the initializer go inside the class definition, but the member definition has to be separate.
C++17 introduces inline variables
C++17 fixes this problem for constexpr static member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.
Proposal P0386 Inline Variables introduces the ability to apply the inline specifier to variables. In particular to this case constexpr implies inline for static member variables. The proposal says:
The inline specifier can be applied to variables as well as to functions. A variable declared
inline has the same semantics as a function declared inline: it can be defined, identically, in
multiple translation units, must be defined in every translation unit in which it is odr-used, and
the behavior of the program is as if there is exactly one variable.
and modified [basic.def]p2:
A declaration is a definition unless
...
it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),
...
and add [depr.static_constexpr]:
For compatibility with prior C++ International Standards, a constexpr
static data member may be redundantly redeclared outside the class
with no initializer. This usage is deprecated. [ Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
C++14 and earlier
In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr this was extended to literal types.
In C++11, we do not need to provide a namespace scope definition for a static constexpr member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2 [class.static.data] which says (emphasis mine going forward):
[...]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. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ]
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition
shall not contain an initializer.
So then the question becomes, is baz odr-used here:
std::string str(baz);
and the answer is yes, and so we require a namespace scope definition as well.
So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. 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.
So baz does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz being an array. This is covered in section 4.1 [conv.lval] which says :
A glvalue (3.10) of a non-function, non-array type T can be
converted to a prvalue.53 [...]
What is applied in the array-to-pointer conversion.
This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.
This is really a flaw in C++11 - as others have explained, in C++11 a static constexpr member variable, unlike every other kind of constexpr global variable, has external linkage, thus must be explicitly defined somewhere.
It's also worth noting that you can often in practice get away with static constexpr member variables without definitions when compiling with optimization, since they can end up inlined in all uses, but if you compile without optimization often your program will fail to link. This makes this a very common hidden trap - your program compiles fine with optimization, but as soon as you turn off optimization (perhaps for debugging), it fails to link.
Good news though - this flaw is fixed in C++17! The approach is a bit convoluted though: in C++17, static constexpr member variables are implicitly inline. Having inline applied to variables is a new concept in C++17, but it effectively means that they do not need an explicit definition anywhere.
My workaround for the external linkage of static members is to use constexpr reference member getters (which doesn't run into the problem #gnzlbg raised as a comment to the answer from #deddebme).
This idiom is important to me because I loathe having multiple .cpp files in my projects, and try to limit the number to one, which consists of nothing but #includes and a main() function.
// foo.hpp
struct foo {
static constexpr auto& baz() { return "quz"; }
};
// some.cpp
auto sz = sizeof(foo::baz()); // sz == 4
auto& foo_baz = foo::baz(); // note auto& not auto
auto sz2 = sizeof(foo_baz); // 4
auto name = typeid(foo_baz).name(); // something like 'char const[4]'
Isn't the more elegant solution be changing the char[] into:
static constexpr char * baz = "quz";
This way we can have the definition/declaration/initializer in 1 line of code.
On my environment, gcc vesion is 5.4.0. Adding "-O2" can fix this compilation error. It seems gcc can handle this case when asking for optimization.