#include <type_traits>
template <class T>
struct foo {
static const std::underlying_type<decltype(a)>::type value = a;
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).
–
–
–
–
–
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;
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.