这是一个最小的示例:
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;
会导致错误,因为即使incomplete_type
不是算术类型(现在,它在逻辑上也不会进入sizeof分支),因为它将使用typeof进行sizeof。 live demo
因此,我想推迟 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;
};
Yes, it works as expected,但它确实乏味且丑陋。
您在这里有优雅的方式吗?
答案 0 :(得分:6)
在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>{});
答案 1 :(得分:5)
如果将double
包装在type_identity
中(在C ++ 20中为a standard utility),并将::type
移动到std::conditional_t<...>
之后,则第二次尝试有效: / p>
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
答案 2 :(得分:4)
我想这不是一个很大的改进,但是您可以使用decltype()
并声明(仅)一些功能,以一种不太丑陋的(IMHO)方式重写您的第三次(有效)尝试。
我的意思是
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>{}) ); };
答案 3 :(得分:3)
您也可以使用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>;
};
答案 4 :(得分:2)
使用TS库基础知识第2版的detection idiom:
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>;
};