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 file
constexpr size_t A::L;
Live example using Clang
For header-only code, and if your class A
is not already a template, you can define a class template A_<T>
with a void
default value, and write a typedef for A
in terms of that
template<class = void>
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<class T>
constexpr size_t A_<T>::L;
using A = A_<>;
NOTE: this business can involve a fair amount of boiler-plate. It is good to note that one can write
template
<
class MyConcept1,
class MyConcept2,
class YetAnotherConcept
// more long and well-documented template parameter names
>
struct A
{
// many static constexpr variabels
};
template<class P1, class P2, class P3 /* many more short parameter names */>
constexpr SomeType A<P1, P2, P3, /* many more */>::some_var;
// many more one-liners.
Template parameters just have formal names, they don't have to be the same everywhere (just put them in the right order everywhere, though!).
来源:https://stackoverflow.com/questions/22172789/passing-a-static-constexpr-variable-by-universal-reference