Im folgenden wird static constexpr
Element L
in Klasse A
initialisiert und dann durch Wert oder (universelle) Referenz übergeben. Letzteres schlägt in Clang, aber nicht in GCC fehl, und das Verhalten ist für Member-/Nicht-Member-Funktionen leicht unterschiedlich. Im Detail:eine statische constexpr-Variable durch universelle Referenz übergeben?
#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)
}
Nach einigen Experimenten in das Problem von einem größeren Programm zu isolieren, wurde mir klar, dass ich aus Versehen der Adresse einer constexpr
Variable verwenden, obwohl ich in dem Wert nur interessiert.
Ich möchte (universelle) Verweis übergeben, so dass der Code generisch ist und mit großen Strukturen ohne zu kopieren funktioniert. Ich dachte, du könntest alles mit einer universellen Referenz durchgehen, aber das scheint hier nicht der Fall zu sein. Ich kann keine separate (out-of-class) Definition für L
verwenden, da dies Teil einer Nur-Header-Bibliothek ist.
So kann man Abhilfe seinen Wert bei Aufruf zu erzeugen, das heißt sagen size_t(A::L)
oder so etwas wie A::get_L()
statt nur A::L
, wo (in Klasse A
)
static constexpr size_t get_L() { return L; }
aber beide Lösungen sehen ein bisschen unbeholfen. In meinem tatsächlichen Code wird der Anruf innerhalb der Klasse gemacht und sieht wie call(0, L, ...)
aus, der ziemlich unschuldig erscheint (0, L
sehen wie Werte aus). Ich möchte den Anruf so einfach wie möglich halten.
Ich denke this question und its follow-up ziemlich viel zu erklären, was passiert. Könnte also jemand vorschlagen, was wäre der sauberste Weg, damit umzugehen?
Eigentlich ist dies unabhängig vom gesamten rvalue/universellen Referenzgeschäft. Der Punkt ist, dass Sie ODR verwenden "A :: L" ohne Angabe einer Definition, und das ist ein Problem mit Lvalue Referenzen. In der Tat müssen Sie für "constexpr" (aber nicht für "const") immer eine "out-of-class" -Definition über den In-Class-Initialisierer bereitstellen. Retagging entsprechend. – TemplateRex
Danke für die erneute Markierung. Ich wusste, Lvalue Referenzen hatten das gleiche Problem, ich wollte nur meine Motivation geben. – iavr
@TemplateRex: Nein, nicht * immer *. Nur wenn das Mitglied odr-used ist. Siehe 9.4.2p3. "...Das Element muss immer noch in einem Namensraumbereich definiert werden, wenn es im Programm verwendet wird und die Namensbereichsdefinition nicht einen Initialisierer enthalten soll. " –