передать статическую переменную constexpr по универсальной ссылке?

В следующих, static constexpr член L инициализируется в классе A и затем передается по значению или (универсальная) ссылка. Последняя ошибка в Clang, но не в GCC, и поведение немного отличается для функций-членов / не-членов. Более подробно:

#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)
}

После некоторых экспериментов по выделению проблемы из более крупной программы я понял, что случайно использую адрес constexpr переменная, хотя меня интересует только значение.

Я хочу передать (универсальную) ссылку, чтобы код был универсальным и работал с большими структурами без копирования. Я думал, что вы можете передать что-нибудь с универсальной ссылкой, но, похоже, это не тот случай. Я не могу использовать отдельное (внеклассное) определение для L потому что это часть библиотеки только для заголовка.

Таким образом, одним из обходных путей может быть создание значения по вызову, то есть, скажем, size_t(A::L) или что-то вроде A::get_L() вместо просто A::Lгде (в пределах класса A)

static constexpr size_t get_L() { return L; }

но оба решения выглядят немного неуклюже. В моем собственном коде вызов сделан внутри класса и выглядит как call(0, L, ...) что выглядит довольно невинно (0, L выглядят как ценности). Я хотел бы сделать звонок максимально простым.

Я думаю этот вопрос а также его продолжение в значительной степени объяснить, что происходит. Так может ли кто-нибудь предложить, какой самый чистый способ справиться с этим?

5

Решение

Вам нужно определить A::L вне его класса в исходном файле

constexpr size_t A::L;

Живой пример используя Clang

Для кода только для заголовка, и если ваш класс A это еще не шаблон, вы можете определить шаблон класса A_<T> с void значение по умолчанию, и напишите typedef для A с точки зрения этого

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

Живой пример.

НОТА: этот бизнес может включать изрядное количество котельной плиты. Приятно отметить, что можно написать

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.

Параметры шаблона просто имеют формальные имена, они не должны быть везде одинаковыми (просто поместите их в правильный порядок хоть везде!)

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]