5

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?

Community
  • 1
  • 1
iavr
  • 7,547
  • 1
  • 18
  • 53
  • Actually, this is independent of the whole rvalue / universal reference business. The point is that you ODR-use `A::L` without providing a definition, and this is a problem using lvalue references as well. In fact, for `constexpr` (but not for `const`) you *always* have to provide out-of-class definition on top of the in-class initializer. Retagging accordingly. – TemplateRex Mar 04 '14 at 13:22
  • Thanks for re-tagging. I knew lvalue references had the same problem, I only wanted to give my motivation. – iavr Mar 04 '14 at 14:16
  • @TemplateRex: No, not *always*. Only if the member is odr-used. See 9.4.2p3. "... 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." – Ben Voigt Apr 04 '14 at 17:05
  • @BenVoigt it appears you are correct, I must have been confused by experiences with older compilers, tnx. – TemplateRex Apr 04 '14 at 17:18

1 Answers1

4

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_<>;

Live Example.

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!).

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Thanks, but as I said in my question I cannot do this (header-only library). – iavr Mar 04 '14 at 13:14
  • @iavr OK, in that case you can use the class template trick and a typedef to define a special case for the class that you want to use. That will all fit into a header. – TemplateRex Mar 04 '14 at 13:18
  • Thanks, I didn't know this trick. Unfortunately, class `A` may well have 10 more template parameters and I may have 2-3 values like `L` so even repeating the entire template stuff 2-3 times is out of the question (far clumsier than intermediate call-by-value). – iavr Mar 04 '14 at 13:25
  • @iavr On the contrary, if `A` already is a class template, then you don't need the extra dummy parameter with a `void` default and the extra typedef. Just provide a `template< /* your list */ > constexpr size_t A< /* your list */>::L;` in the header. Data members of class templates can reside in headers no problem. But yes, you need to do that for every static constexpr variable, no way around that. – TemplateRex Mar 04 '14 at 13:27
  • Yes, I understand. The problem is that `/* my list */` may be 2-3 lines of code, so I'd like to only write once. – iavr Mar 04 '14 at 13:29
  • @iavr that should be minor annoyance at best, in fact you can use one-letter abbreviations for those parameters for the variable definitions and use more descriptive names for the class template definition itself. If all else fails, just write a macro :-) – TemplateRex Mar 04 '14 at 13:31
  • Ok, here is just one example, of which I may have 2-3 different specializations: `template class join_trav_impl , R, T, D, TR, sizes >;`. It is then probably a matter of style. Thanks anyway :-) – iavr Mar 04 '14 at 13:37
  • @iavr updated the answer to reflect your concern for too much typing. Specializations really come on top of that, ouch, yes I see your pain, but editors and macros should be able to handle that. – TemplateRex Mar 04 '14 at 13:37
  • @iavr btw, that example with so many template parameters, cries out for [policy-based design](http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315) where you factor it into orthogonal components and specialize each of those much smaller building blocks. The main class template `A` just assemblies them by using inheritance and extracts the relevant parts using traits. – TemplateRex Mar 04 '14 at 13:42
  • Thanks again for the pointer. I am already trying to do this at least partially, e.g. parameter `TR` above is such a traits class with 10 more definitions. Packs `V...` and `N...` I need them handy (that's why I specialize). `Q,R,T` are the main parameters following a specific pattern for say 10 classes like this one. Plus, by adding 2-3 parameters with defaults you get an one-letter representation to re-use in whatever the class inherits or would define by `using`. E.g. parameter `D` has default `typename D = join_trav `, ouch :-) – iavr Mar 04 '14 at 13:57
  • -1 This is perhaps a workaround, but not a generic solution. What if there is no source file, i.e. if the `static constexpr` variable is defined in some std header? – Walter Mar 14 '14 at 12:26
  • @Walter I don't quite understand your remark: for header-only code, you can define the `static constexpr` variable for a template class, and use a typedef to generate a regular class. Boost uses this trick all over the place to keep code header-only. Could you please clarify what exactly you think is worthy of a downvote? – TemplateRex Mar 14 '14 at 12:28