Почему этот constexpr не является константой шаблона переменной функции? - PullRequest
0 голосов
/ 22 января 2019

В моем классе (который является шаблоном класса variadic) мне нужно constexpr для sizeof() самого большого типа, переданного в шаблоне variadic. Как это:

template<class... Types>
class DiscriminatedUnion
{
.
.
.
static constexpr auto value = maxSizeOf<Types...>();

Код, который я придумал для maxSizeOf(), следующий:

template <class T>
static constexpr T static_max(T a, T b) {
    return a < b ? b : a;
}

template <class T, class... Ts>
static constexpr T static_max(T a, Ts... bs) {
    return static_max(a, static_max(bs...));
}

template <class T>
static constexpr int maxSizeOf() {
    return sizeof(T);
};

template <class T, class... Ts>
static constexpr int maxSizeOf() {
    return static_max(sizeof(T), maxSizeOf<Ts...>());
};

Но в Visual Studio 2017 я получаю ошибку компиляции «выражение не вычислено как константа».

Я не уверен, что не позволяет выражению быть постоянным. Я пытался компилировать разные вещи, чтобы убедиться, что они могут быть постоянными. Я попытался использовать sizeof() с параметром шаблона в функции constexpr, которая работает, что я ожидаю, так как размер типов всегда известен во время компиляции. Целочисленная арифметика и сравнение, кажется, действительны в функции constexpr, но я попробовал еще раз для проверки. Затем я попытался использовать целочисленную арифметику в методе шаблонов переменных, без sizeof(), со следующим:

template <class T>
static constexpr int maxSizeOf(int n) {
    return n;
};

template <class T, class... Ts>
static constexpr int maxSizeOf(int n) {
    return static_max(n, maxSizeOf<Ts...>(n + 1));
};

static constexpr int numBytes = maxSizeOf<Types...>(1);

И это не работает. Так что я думаю, что это как-то связано с расширением шаблона метода variadic. Но это должно быть в состоянии сделать константу времени компиляции, потому что пакеты вариабельных шаблонов всегда расширяются во время компиляции. Кто-нибудь знает, почему это не может быть constexpr?

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Проблема вашего кода в том, что когда вы звоните max_sizeof<T>() с одним T типами, оба

template <class T>
static constexpr int maxSizeOf() {
    return sizeof(T);
};

и

template <class T, class... Ts>
static constexpr int maxSizeOf() {
    return static_max(sizeof(T), maxSizeOf<Ts...>());
};

совпадают.Таким образом, компилятор не может выбрать правильный.

Вы можете решить с помощью if constexpr ( sizeof...(Ts) ), как предлагает dontpanic, но if constexpr доступен только начиная с C ++ 17.

Возможное (и элегантное, IMHO) решение, работающее также в C ++ 11 и C ++ 14, заключается в удалении функции only-one-type и добавлении следующей функции нулевого типа

template <int = 0>
static constexpr std::size_t maxSizeOf()
 { return 0u; };

Thisспособ, когда вы вызываете maxSizeOf<Ts...>(), когда sizeof...(Ts) > 0u, вызывается версия одного или нескольких типов;когда sizeof...(Ts) == 0u (то есть: когда список Ts... равен empy), int = 0 (без типов) совпадает.

Другое предложение: sizeof() - это значение std::size_t, поэтому лучше, еслиmaxSizeOf() return a std::size_t

Ниже приводится полностью рабочее (также C ++ 11) решение

#include <iostream>

template <typename T>
static constexpr T static_max (T a, T b)
 { return a < b ? b : a; }

template <typename T, typename ... Ts>
static constexpr T static_max (T a, Ts ... bs)
 { return static_max(a, static_max(bs...)); }

template <int = 0>
static constexpr std::size_t maxSizeOf()
 { return 0u; };

template <typename T, typename ... Ts>
static constexpr std::size_t maxSizeOf()
 { return static_max(sizeof(T), maxSizeOf<Ts...>()); };

template <typename ... Ts>
struct foo
 { static constexpr auto value = maxSizeOf<Ts...>(); };

int main ()
 {
   std::cout << foo<int, long, long long>::value << std::endl;
 }

Но, как заметил Ашеплер (спасибо!), Это решениеработает, но не использует вариационную версию static_max().

Альтернативой, которая использует вариационную версию static_max(), является перезапись вариационной версии maxSizeOf() не рекурсивным способом, апросто распаковываем список переменных следующим образом

template <typename ... Ts>
static constexpr std::size_t maxSizeOf()
 { return static_max(sizeof(Ts)...); } 

Теперь это базовый вариант (версия нулевого типа) maxSizeOf(), который больше не используется и может быть удален.

В любом случаеКак предлагает NathanOliver, вы можете использовать std::max() (версия, получающая список инициализаторов), которая, начиная с C ++ 14, равна constexpr.

Итак, начиная с C ++ 14, выможно просто написать

#include <algorithm>
#include <iostream>

template <typename ... Ts>
struct foo
 { static constexpr auto value = std::max({sizeof(Ts)...}); };

int main ()
 {
   std::cout << foo<int, long, long long>::value << std::endl;
 }
0 голосов
/ 22 января 2019

"выражение не было константой."кажется не первопричина.Ваши static_max и maxSizeOf нуждаются в модификациях, чтобы компилятор был доволен.Вы можете обратиться к этой записи , чтобы узнать, как это сделать в соответствии с различными стандартами C ++.

Например:

template <class T, class... Ts>
static constexpr T static_max(T a, Ts... bs) {
    if constexpr (sizeof...(Ts) == 0)
        return a;
    else
        return std::max(a, static_max(bs...));
}

template <class T, class... Ts>
static constexpr int maxSizeOf(int n) {
    if constexpr (sizeof...(Ts) == 0)
        return n;
    else
        return static_max(n, maxSizeOf<Ts...>(n + 1));
};

На самом деле нам не нужно static_max совсем.Все, что нам нужно здесь, - это найти максимум в двух значениях, и std::max уже есть.

РЕДАКТИРОВАТЬ: похоже, нам тоже не нужно maxSizeOf ... как упоминал Натан вкомментарии, std::max может иметь дело и с initializer_list.

...