Используйте sizeof с неполным типом внутри std :: conditional

Вот минимальный пример:

struct incomplete_type;

template<typename T>
struct foo
{
using type = std::conditional_t<std::is_arithmetic_v<T>,
std::conditional_t<sizeof(T) < sizeof(void*), int, float>,
double>;
};

foo<incomplete_type> f; вызовет ошибку, потому что это будет делать sizeof с типом, даже если incomplete_type это не арифметический тип (т. е. он не будет соответствовать размеру ветви логически). живое демо

Итак, я хочу откладывать sizeof:

первая попытка (неудача)

template<typename T>
auto
foo_aux()
{
if(sizeof(T) < sizeof(T*))
return 0;
else
return 0.0f;
}

conditional_t<std::is_arithmetic_v<T>, decltype(foo_aux<T>()), double> по-прежнему вызвать ту же ошибку.

вторая попытка (неудача)

template<typename T, bool>
struct foo_aux_aux
{
using type = float;
};
template<typename T>
struct foo_aux_aux<T, true>
{
using type = int;
};

template<typename T, bool = false>
struct foo_aux : foo_aux_aux<T, sizeof(T) < sizeof(void*)>
{};

conditional_t<std::is_arithmetic_v<T>, typename foo_aux<T>::type, double> по-прежнему вызвать ту же ошибку.

третья попытка (успех)

template<typename T, bool comp>
struct foo_aux_aux
{
using type = float;
};
template<typename T>
struct foo_aux_aux<T, true>
{
using type = int;
};

template<typename T, bool isArithmeticType>
struct foo_aux
{
using type = double;
};

template<typename T>
struct foo_aux<T, true>
{
using type = typename foo_aux_aux<T, sizeof(T) < sizeof(void*)>::type;
};

Да, работает как положено, но это действительно утомительно и безобразно.

У вас есть элегантный способ здесь?

5

Решение

В C ++ 17 вы можете использовать if constexpr делать вычисления типа. Просто поместите тип в фиктивный контейнер и используйте вычисление значения, а затем извлекайте тип через decltype,

struct foo может быть реализовано как:

template<class T>
struct type_ {
using type = T;
};

template<class T>
struct foo {
auto constexpr static type_impl() {
if constexpr (std::is_arithmetic<T>{}) {
if constexpr (sizeof(T) < sizeof(void*)) {
return type_<int>{};
} else {
return type_<float>{};
}
} else {
return type_<double>{};
}
}

using type = typename decltype(type_impl())::type;
};

static_assert(std::is_same<foo<incomplete_type>::type, double>{});
6

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

Ваша вторая попытка работает, если вы заверните double в type_identity (который стандартная утилита в C ++ 20) и двигайся ::type после std::conditional_t<...>:

template<typename T, bool>
struct foo_aux_aux
{
using type = float;
};
template<typename T>
struct foo_aux_aux<T, true>
{
using type = int;
};

template<typename T, bool = false>
struct foo_aux : foo_aux_aux<T, sizeof(T) < sizeof(void*)>
{};

template<typename T>
struct type_identity { using type = T; };

typename std::conditional_t<std::is_arithmetic_v<T>, foo_aux<T>, type_identity<double>>::type
5

Полагаю, это не очень хорошее улучшение, но вы можете переписать свою третью (рабочую) попытку менее уродливым (ИМХО) способом, используя decltype() и объявив (только) некоторые функции.

Я имею в виду что-то

struct incomplete_type;

constexpr float baz (std::false_type);
constexpr int baz (std::true_type);

template <typename>
constexpr double bar (std::false_type);

template <typename T>
constexpr auto bar (std::true_type)
-> decltype(baz<std::bool_constant<(sizeof(T) < sizeof(void*))>{});

template<typename T>
struct foo
{ using type = decltype( bar<T>(std::is_arithmetic<T>{}) ); };
4

Вы также можете использовать SFINAE:

template <class T1, class T2, class = int (*)[sizeof(T1) < sizeof(T2)]>
constexpr bool DeferSizeof(int) {
return true;
}
template <class, class>
constexpr bool DeferSizeof(...) {
return false;
}

template<typename T>
struct foo
{
using type = std::conditional_t<std::is_arithmetic_v<T>,
std::conditional_t<DeferSizeof<T, void *>(0), int, float>,
double>;
};
3

С идиома обнаружения из библиотеки TS Основы v2:

template <typename T>
using size_of = std::integral_constant<std::size_t, sizeof(T)>;

template <typename T>
struct foo
{
using type = std::conditional_t<
std::is_arithmetic_v<T>,
std::conditional_t<
std::experimental::detected_or_t<
std::integral_constant<std::size_t, 0>, size_of, T
>{} < sizeof(void*),
int, float>,
double>;
};

DEMO

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector