C++ Linker Error With Class static constexpr
I don't think this is a bug. If you change the constexpr
to const
, it still fails, with the exact same error.
You've declared S::X
, but not defined it anywhere, so there's no storage for it. If you do anything with it that needs to know the address of it then you'll need to define it somewhere also.
Examples:
int main() {
int i = S::X; // fine
foo<S::X>(); // fine
const int *p = &S::X; // needs definition
return std::min(S::X, 0); // needs it also
}
The reason for this is that constexpr
can be evaluated at compile time, but it's not required to be evaluated as such, and can equally happen at runtime. It doesn't instruct "the compiler to always treat the symbol as an expression", it hints that it would be sensible and permissible to do so if the compiler felt like it.
Linker error for constexpr static member variable in gcc and clang
Before C++17
All the initializations of A
s invoke the A::A(B )
constructor, which require copying a B
, which uses the compiler-generated B::B(B const&)
. Binding a variable to reference (A::bbb
) is an odr-use of that variable. The specific rule, from [basic.def.odr] is:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the
lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e, where
either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).
The first call to the copy constructor would involve binding A::bbb
to a reference, which is neither an lvalue-to-rvalue conversion nor a discarded-value expression, so it's odr-used.
The most important rule is:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (6.4.1); no diagnostic required.
A::bbb
is odr-used, but lacks a definition, so we're violating that rule - commonly referred to as an odr violation. But since the compiler is not required to issue a diagnostic in this case ("no diagnostic required", or NDR for short), the program is undefined behavior. These kinds of issues can be frustrating at times for a programmer, but they're arbitrarily difficult for the compiler to diagnose - so it's something that we have to live with.
It is likely that on higher optimization levels, the compilers simply elide the copy, so a call to B::B(B const&)
for A::bbb
isn't necessary... As to why different initializations are treated differently? Probably as a result of different optimization passes. Ultimately, it doesn't change the fact that this is an odr violation - regardless of whether the code compiles and links.
After C++17
As a result of p0386, static constexpr
data members are implicitly inline, which means that now A::bbb
does have a definition and now there is no odr-violation. C++17 is cool.
Linker error with constexpr static member variable
constexpr static
members can only be initialised inside the class body if the initialiser is also constexpr
.
Your fun
function contains a call to sin
which, like most of the standard maths functions, is not constexpr
.
Having a constexpr static string gives a linker error
Should work:
#include <iostream>
struct Test { static constexpr char text[] = "Text"; };
constexpr char Test::text[];
int main()
{
std::cout << Test::text << std::endl;
}
In standard (n4140 §9.4.2/3) you can find:
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.
Undefined reference error for static constexpr member
This behaviour is vexing me time and again. The cause of the trouble is that your
A(int n): v(n, A::kDefaultValue) {}
odr-uses the static constexpr
member, since the constructor of v
takes a constant reference second argument. Odr-usage requires a definition somewhere, i.e.
const int A::kDefaultValue;
in some compilation unit (which is compiled and linked to main()
). This requirement has been dropped in C++17 and the corresponding definition (as above) deprecated.
However, a definition is not always possible (for example for members of class templates) and the simplest way to avoid both the definition and your error is
A(int n): v(n, int(A::kDefaultValue)) {}
which creates a temporary to be passed to the constructor of v
(but since the latter is fully inline, the compiler may optimise that away).
Different compilation + linking errors for static and constexpr between clang and gcc
So, without inline
variables, I was able to get something achieving your goals working. The basic idea is to have a "backend" struct
to hold static
members and then fully specialize that struct
to your options. This method has the added benefit of causing a compiler error if you attempt to access a member of the backend using a template parameter that has not been defined yet.
The header file would look like
#ifndef TEMPLATED_HEADER_HPP
#define TEMPLATED_HEADER_HPP
namespace template_header {
/**
* the "primary" template of the backend struct
* notice I leave the variable we want undefined!
* if you prefer to have a default for all other values of dim1
* you would put that here
*/
template<int dim1>
struct backend {};
template<>
struct backend<2> {
static constexpr int dim2 = 3;
};
template<>
struct backend<3> {
static constexpr int dim2 = 5;
};
/**
* Helper constexpr
* this is optional, but makes code outside this file more readable
* also, I named it in a way for your source files to be unchanged.
*/
template <int dim1>
constexpr dim2 = backend<dim1>::dim2;
}
#endif
I have a working version of this idea on Compiler Explorer which verifies that both this works with both GCC and clang. If you were to try to call dim2<dim1>
where dim1
does not equal 2 or 3, then a compiler error complaining about backend<dim1>::dims not defined
will be generated. You could add other members to the backend
specializations, taking care to keep the names the same across the different values of dim1
.
Total side note, why are you setting ar1 = {0};
? From my reading of the std array reference, this would only set the first element in the array to 0.
Constexpr class function definition linking error
constexpr
functions are implicitly inline
and therefore need to be defined in every translation unit where they are used. That usually means that the definition should go into a header shared between the translation units using the function.
Also, in order for constexpr
to be of any use, the function needs to be defined before its potential use in a constant expression. Therefore it usually only really makes sense to define it directly on the first declaration, i.e. here in the class
definition in the header.
Related Topics
C++ Streams Confusion: Istreambuf_Iterator VS Istream_Iterator
Pointing to a Function That Is a Class Member - Glfw Setkeycallback
Delete All Items from a C++ Std::Vector
How to Assign Multiple Values into a Struct at Once
Optimize Template Replacement of a Switch
Performance Penalty for Working with Interfaces in C++
C++ Why the Assignment Operator Should Return a Const Ref in Order to Avoid (A=B)=C
Operator << Must Take Exactly One Argument
How to Use Threads to Speed Up File Reading
C++ Can Compilers Inline a Function Pointer
When to Use 'Asio_Handler_Invoke'
How to Create Objects While Adding Them into a Vector
How to Validate That a String Is a Valid Ipv4 Address in C++
Use of Typename Keyword with Template Function Parameters
Check If a Type Is from a Particular Namespace
Copying Derived Entities Using Only Base Class Pointers, (Without Exhaustive Testing!) - C++