Here is a minimal example:
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;
will cause error because it will do sizeof with type, even though incomplete_type
is not a arithmetic type(iow, it will not go into sizeof branch logically). live demo
So, I want to defer 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>
still trigger the same error.
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>
still trigger the same error.
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;
};
Yes, it works as expected, but its really tedious and ugly.
Do you have an elegant way here?
In C++17, you can use if constexpr
to do type computation. Just wrap the type into a dummy container and use value computation, then retrieve the type via decltype
.
struct foo
could be implemented like:
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>{});
Your second attempt works if you wrap double
in type_identity
(which is a standard utility in C++20) and move ::type
after 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
Not a great improvement, I suppose, but you can rewrite your third (working) attempt in a little less ugly (IMHO) way using decltype()
and declaring (only) some functions.
I mean something as
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>{}) ); };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With