In the following, static constexpr
member L
is initialized in-class A
and then passed by value or by (universal) reference. The latter fails in Clang but not in GCC, and behaviour is slightly different for member/non-member functions. In more detail:
#include <iostream>
using namespace std;
struct A
{
static constexpr size_t L = 4;
template <typename T>
void member_ref(T&& x) { cout << std::forward<T>(x) << endl; }
template <typename T>
void member_val(T x) { cout << x << endl; }
};
template <typename T>
void ref(T&& x) { cout << std::forward<T>(x) << endl; }
template <typename T>
void val(T x) { cout << x << endl; }
int main ()
{
A().member_ref(A::L); // Clang: linker error: undefined reference to `A::L'
A().member_val(A::L); // fine (prints 4)
ref(A::L); // Clang: compiles/links fine, no output
val(A::L); // fine (prints 4)
}
After some experimentation in isolating the problem from a larger program, I realized that I am accidentally using the address of a constexpr
variable, although I am only interested in the value.
I want to pass by (universal) reference so that the code is generic and works with large structures without copying. I thought that you could pass anything with a universal reference but it appears this is not the case here. I cannot use a separate (out-of-class) definition for L
because this is part of a header-only library.
So one workaround can be to generate a value upon call, i.e. say size_t(A::L)
or something like A::get_L()
instead of just A::L
, where (within class A
)
static constexpr size_t get_L() { return L; }
but both solutions look a bit clumsy. In my actual code the call is made within the class and looks like call(0, L, ...)
which appears quite innocent (0, L
look like values). I'd like to keep the call as simple as possible.
I think this question and its follow-up pretty much explain what is happening. So could anyone suggest what would be the cleanest way to deal with this?
You need to define
A::L
outside its class in a source fileLive example using Clang
For header-only code, and if your class
A
is not already a template, you can define a class templateA_<T>
with avoid
default value, and write a typedef forA
in terms of thatLive Example.
NOTE: this business can involve a fair amount of boiler-plate. It is good to note that one can write
Template parameters just have formal names, they don't have to be the same everywhere (just put them in the right order everywhere, though!).