Static data member: "const declaration / constexpr definition" does it work? - c++

The following piece of code tries to provide a constexpr
static data member of type X that belongs to the type X itself.
In the standard library (C++20), there seem to be such examples
with (at least) the class 'std::strong_ordering' and its few static
constexpr members named 'equal', 'less', 'greater' and 'equivalent'.
I am wondering if (and how) this can be achieved without compiler magic.
A direct declaration (including the definition) does
not seem to work with any compiler nor to be valid C++.
That being said, a declaration as 'const' followed later
(outside of the class) by a definition as 'constexpr' seems
to work fine with GCC and at least in some cases with Clang.
My questions are the following:
Is the trick consisting of a "const declaration" followed by
a "constexpr definition" forms valid C++ code that actually provides
a valid static constexpr data member of type X inside the class X itself?
The non template version (type Foo) compiles with GCC and Clang
while the template version (type Bar<0>) only compiles with GCC.
Is there any rule that would make the non template version
valid C++ code and the template one non valid C++ code?
Can the error generated by Clang be considered as a compiler bug?
Source code (C++17):
// With this non template struct,
// It compiles successfully with GCC and Clang.
struct Foo
{
// A data member.
int val;
// A static data member.
// It is declared here as 'const'
// and defined below as 'constexpr'.
static Foo const instance;
// A constexpr constructor.
constexpr Foo(int value) noexcept : val{ value } {}
};
// With this non template struct,
// It compiles successfully with GCC
// but it generates an error with Clang.
template<int N>
struct Bar
{
// A data member.
int val;
// A static data member.
// It is declared here as 'const'
// and defined below as 'constexpr'.
static Bar const instance;
// A constexpr constructor.
constexpr Bar(int value) noexcept : val{ value } {}
};
// Definition of the static
// data member of the struct Foo.
// Note that it is defined here as 'constexpr'
// while it was declared above only as 'const'.
constexpr Foo const Foo::instance{32};
// Definition of the static data
// member of the template struct Foo.
// Note that it is defined here as 'constexpr'
// while it was declared above only as 'const'.
template<int N>
constexpr Bar<N> const Bar<N>::instance{32};
// The main function.
int main()
{
// Init a constexpr const reference to
// the static data member object of type Foo.
constexpr Foo const& foo{ Foo::instance };
// Init a constexpr const reference to
// the static data member object of type Bar<0>.
constexpr Bar<0> const& bar{ Bar<0>::instance };
// This compile time check
// works fine with GCC and Clang.
static_assert(foo.val == 32);
// This compile time check works fine with GCC
// but generates a compilation error with Clang.
static_assert(bar.val == 32);
// Return zero.
return 0;
};
Previous StackOverflow related questions:
I am quoting two StackOverflow questions which are
related but (in my view) do not clearly answer my question.
This one tries to achieve the same goal as me but does not seem
to mention the "const declaration / constexpr definition" trick. Can't a class have static constexpr member instances of itself?
This one mentions the "const declaration / constexpr definition" trick but
is not clearly answering the question whether it is valid C++ code or not. Static const declaration, constexpr definition of variable, valid c++?

Regarding the example of std::strong_ordering: the C++ standard itself states that these static data members are declared with const and defined outside the class with inline constexpr, so that should be considered strong evidence that this technique does, in fact, work in standard C++. See e.g. here.
I could find nothing in the standard that justifies Clang rejecting this only when it's templated. It appears that a bug has already been filed against Clang that identifies the specific issue, although it doesn't seem to have been confirmed by the maintainers.

Related

Should a definition inside template class be instantiated if it is not used?

template <typename T>
struct A
{
static constexpr T obj {};
static constexpr bool noexcept_copy = noexcept( T{obj} );
static void UsesCopy() { T{obj}; }
static constexpr int C = 1;
};
struct NoCopy
{
constexpr NoCopy() = default;
NoCopy(const NoCopy&) = delete;
};
int main()
{
return A<NoCopy>::C;
}
The code above is successfully compiled by GCC, but Clang gives a compilation error:
tmp.cpp:6:57: error: call to deleted constructor of 'NoCopy'
static constexpr bool noexcept_copy = noexcept( T{obj} );
^~~~~~
tmp.cpp:20:16: note: in instantiation of template class 'A<NoCopy>' requested here
return A<NoCopy>::C;
^
tmp.cpp:15:9: note: 'NoCopy' has been explicitly marked deleted here
NoCopy(const NoCopy&) = delete;
^
1 error generated.
The A::UsesCopy function uses copy constructor as well, but the compiler does not complain about the usage of deleted function there. What is the difference between UsesCopy function and noexcept_copy constexpr? Both use copy constructor of NoCopy class and both are not used but the constexpr definition produces a compilation error, the function definition does not.
PS. Clang compiles the code above with -std=c++17 or -std=c++2a, but not with -std=c++11 or -std=c++14.
I think the correct approach to deal with this issue is similar to what is details in this pre-C++17 answer to a question about the order of initialization of static constexpr templated data members.
TL;DR - yes GCC, got it right, Clang tries to resolve the copy c'tor even though it is not allowed.
To summarize:
C++14
In p9.4.2.3 - Static data members, we have:
[...] 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 in the program and the namespace scope definition shall not contain an initializer.
So the declaration of static constexpr data member is just a declaration and is not a definition - even though it has an initializer. A definition is required to cause initialization, and none is provided in the original OP code.
To drive home the point, we have the previously quoted p14.7.1 - Templates - Implicit Instantiation (emphasis mine):
The implicit instantiation of a class template specialization causes the implicit instantiation of the
declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members and member template
GCC correctly does not initialize noexcept_copy as there is no definition for it, only a decleration - it is never "defined in a namespace scope", as required by p9.4.2.3.
C++17
So what changed? Well, as far as I can tell - nothing major, but there were some changes around how static data members are defined, in C++17, by adopting P0389R2 whose purpose - as far as I understand - it so introduce "inline variables" which can be declared simply and then odr-used in multiple translation units with the same storage, i.e. there's only one instance of the variable initialized with the initializer in the declaration - this is similar to "static field initialization" in other languages such as Java and makes it easier to have singleton eager initialization.
Here's from the spec:
A declaration is a definition unless [...] it declares a non­-inline static data member in a class definition.
So we explicitly specify that inline static data member's declaration is a definition. No need for an external definition out of the class scope. This certainly makes it easier for programmers.
But what inline has to do with constexpr? The adoption of the proposal also resulted in this specification in p10.1.5.1:
A function or static data member declared with the constexpr specifier is implicitly an inline function or variable.
So this change actually makes it pretty explicit that the declaration of static constexpr bool noexcept_copy is also a definition - which we mustn't instantiate in case of an implicit template instantiation.
I'm guessing this is a strong enough signal for Clang developers to not initialize the static constexpr data members.
BTW, the example in C++17 Appendix D p.1 explains this rather explicitly:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
From this example we learn that, in C++ 2014 a declaration of a static constexpr was not a definition, and in order for the initialization to take place, you must have a definition in a namespace scope.
So Clang is wrong outputing an error in their C++14 implementation because in the OP code, not only is it wrong to implicitly instantiate the template class static data member - there isn't even a definition for it so it shouldn't have been instantiated even if it wasn't a template class.
I think this problem is referred by the following standard lines on template :
An implementation shall not implicitly instantiate a function
template, a variable template, a member template, a non-virtual member
function, a member class, a static data member of a class template, or
a substatement of a constexpr if statement, unless such instantiation
is required.
So the Clang compiler do the right things. GCC in this case erronously don't give you an error.
PS. Clang compiles the code above with -std=c++17 or -std=c++2a, but
not with -std=c++11 or -std=c++14.
In fact with the c++17 there was a change in the template standard.

Does the C++ standard define if in-function definitions of member functions in structs must have static linkage?

I have a static function defined within a struct defined within a function. I want to get a template function pointer to the inner function.
Consider the following example:
template <class Func, Func GetToken>
struct token_user {
void foo () { GetToken(); } // Do something with token.
};
struct generator_out {
constexpr static const auto get_token() {return 0;}
};
int main() {
struct generator_in {
constexpr static const auto get_token() {return 0;}
};
// Works fine
token_user<decltype(&generator_out::get_token), &generator_out::get_token>();
// Fails with GCC, works with clang and msvc
token_user<decltype(&generator_in::get_token), &generator_in::get_token>();
}
I tested this with my local MSVC 2017 Compiler and also with the clang 6.0 and gcc 8.1 compiler on wandbox.org. MSVC and clang work, gcc doesn't.
Who is correct? Are clang and msvc too forgiving and bend the c++ standard or has gcc simply not implemented this yet? (Or is it maybe even a bug?) What does the standard say about this?
EDIT:
Error message from gcc:
prog.cc: In function 'int main()':
prog.cc:15:76: error: 'main()::generator_in::get_token' is not a valid template argument for type 'const int (*)()' because 'static constexpr const auto main()::generator_in::get_token()' has no linkage
token_user<decltype(&generator_in::get_token), &generator_in::get_token>(); // Fails with GCC, works with clang and msvc
GCC is right, generator_in::get_token has no linkage and cannot be used outside the scope in which it is declared, and thus cannot be used as an argument to instantiate token_user.
See [basic.link]/8 and /9 (I'm including only relevant parts):
... except as noted, a name declared at block scope has no linkage.
Example:
void f() {
struct A { int x; }; // no linkage
}
This is important because member functions have the linkage of the class. [basic.link]/5:
... a member function, static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes ([dcl.typedef]), has the same linkage, if any, as the name of the class of which it is a member.
So both generator_in and generator_in::get_token have no linkage.
And finally: [basic.link]/2.3:
— When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.

Initializer "sizeof(T)" of inline static auto... Does it need instantiation?

What should happen if an expression's type is not dependent, but we use it to initialize a static auto variable? GCC and Clang differ in their behavior
template<typename T>
struct A {
static inline auto x = sizeof(T{}.f);
};
A<int> a;
GCC doesn't raise an error. But Clang thinks that this is invalid because it instantiates the operand of "sizeof". GCC appears to skip that step because sizeof(T{}.f) always has type size_t (not type dependent), so it already knows type of x without instantiation. Both compilers conformly reject the program if we refer to x, for example by (void) a.x;.
Does it even have to resolve the type of x at all? With C++14 upwards the language allows keeping things (like functions) with a "placeholder type" and doing delayed instantiation to find out about the actual return type later on, if I remember correctly. Does it have to apply this to x aswell, so keeping x with a placeholder type till we refer to a.x?
What compiler is correct according to the Standards?
EDIT
Someone asked
uhm, shouldnt' this be equivalent to this ?
template<typename T>
struct A {
static const std::size_t x;
};
template<typename T>
inline constexpr std::size_t A<T>::x = sizeof(T{}.f);
The difference, and what concerns me in my question, is that the static data member in my question is auto. Therefore, in order to know the type of x, you need to know the type of the initializer. Clang appears to instantiate the initializer eagerly in order to get the type. But GCC doesn't, apparently? I would like to understand what's going on.
From [temp.inst]/3:
Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
Simply writing A<int> a; does noes not use A<int>::x in a way that requires its definition to exist, so its initialization should not occur. gcc is correct.

Undefined reference when changing from GCC 4.6 to 4.7

I had to switch from g++-4.6 to 4.7 (so I can use some C++11 features). Now, the compiler complains:
In function WordJIT<float>::WordJIT(): undefined reference to JitRegType<float>::Val_t
I am wondering if something has changed between these compiler version that can affect symbol resolving. Or is the language better implemented in the new version (4.7) and what I am doing is wrong: (same code compiles with 4.6)
jit.h
class Jit {
public:
enum RegType { f32=0,f64=1,u16=2,u32=3,u64=4,s16=5,s32=6,s64=7 };
// ...
};
template <class T> struct JitRegType {};
template <> struct JitRegType<float> { static const Jit::RegType Val_t = Jit::f32; };
wordjit.h
#include "jit.h"
template<class T>
class WordJIT
{
WordJIT() {
mapReg.insert( std::make_pair( JitRegType<T>::Val_t , jit.getRegs( JitRegType<T>::Val_t , 1 ) ) );
}
private:
typedef std::map< Jit::RegType , int > MapRegType;
mutable MapRegType mapReg;
};
Edit:
Is static const okay in a header file or should one use constexpr ?
Is there a way to declare also Val_t in the class declaration of JitRegType but not actually define it ?
Per 9.4.2p3:
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer [...] The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
So you need to add to your program (probably in jit.cpp):
const Jit::RegType JitRegType<float>::Val_t;
This is so that if the static const member is used in a context where a reference to it is required, there exists a unique definition for the linker to refer to (the same as any static member that is not a member of a class template or class template partial specialization).
The issue is discussed in depth on the gcc wiki.
Note that both gcc 4.6 and 4.7 are behaving sensibly; it is just that gcc 4.6 chooses to inline the value of JitRegType<float>::Val_t where gcc 4.7 chooses not to (or possibly is inlining it but emitting a linker reference to the definition as well). It's a little difficult to tell whether the implementation is required to issue a diagnostic; 9.4.2p3 describes a diagnosable rule but then 9.4.2p4 (implicitly referring to non-const static data members) says that a diagnostic is not required. Either way, as a quality of implementation issue it's better that a compiler issue a diagnostic than not.

Is it legal C++ to pass the address of a static const int with no definition to a template?

I'm having trouble deciding whether not this code should compile or if just both compilers I tried have a bug (GCC 4.2 and Sun Studio 12). In general, if you have a static class member you declare in a header file you are required to define it in some source file. However, an exception is made in the standard for static const integrals. For example, this is allowed:
#include <iostream>
struct A {
static const int x = 42;
};
With no need to add a definition of x outside the class body somewhere. I'm trying to do the same thing, but I also take the address of x and pass it to a template. This results in a linker error complaining about a lack of definition. The below example doesn't link (missing a definition for A::x) even when it's all in the same source file:
#include <iostream>
template<const int* y>
struct B {
static void foo() { std::cout << "y: " << y << std::endl; }
};
struct A {
static const int x = 42;
typedef B<&x> fooness;
};
int main()
{
std::cout << A::x << std::endl;
A::fooness::foo();
}
Which is bizarre since it works as long as I don't pass the address to a template. Is this a bug or somehow technically standards compliant?
Edit: I should point out that &A::x is not a runtime value. Memory is set aside for statically allocated variables at compile time.
To be a well formed program you stil have to have the defintion of the static variable (without an initializer in this case) if it actually gets used, and taking the address counts as a use:
C++2003 Standard: 9.4.2 Static data members Paragraph 4 (bold added)
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 expressions. 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
You are trying to pass a runtime value to a template, that's not possible. The only allowed template parameters are types (class/typename) or integral constant values (int/bool/etc).
Interesting, it compiled fine for me on VS 2008. I kind of assumed that the error came from the typedef because at compile time when it tries to compile 'B' with &x as the template type it doesn't then know where the address of x will be. Still... it compiles and gives a reasonable output.
I could see how one might expect this to compile anyway.
The address of a static const isn't really a runtime value and can be fully resolved at link time.