此代码适用于VS2013和其他编译器(测试过的clang 3.4和gcc 4.8),但无法在VS2012中编译:
#include <type_traits>
#include <cstdio>
// error C4519: default template arguments are only allowed on a class template
template<typename E, typename std::enable_if<std::is_enum<E>::value>::type* = nullptr>
typename std::underlying_type<E>::type to_integral(E e)
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
template<typename E, typename std::enable_if<!std::is_enum<E>::value>::type* = nullptr>
E to_integral(E e)
{
return e;
}
enum class MyEnum : int { A = 5 };
int main()
{
auto a = to_integral(42);
auto b = to_integral(MyEnum::A);
printf("%d\n", a);
printf("%d\n", b);
}
如何在VS2012中编写to_integral
?可能吗?我尝试在return参数上使用enable_if
并作为参数,但是underlying_type
出现在函数签名中,编译器往往不喜欢非枚举类型。
答案 0 :(得分:1)
将enable_if
放入返回类型:
template<bool b, template<class>class X, class T>
struct invoke_if {};
template<template<class>class X, class T>
struct invoke_if<true, X, T> {
typedef typename X<T>::type type;
};
template<typename E>
typename invoke_if< std::is_enum<E>::value,std::underlying_type, E >::type
to_integral(E e) {
return static_cast<typename std::underlying_type<E>::type>(std::move(e));
}
或更简单:
template<typename E>
typename std::enable_if< std::is_enum<E>::value,std::underlying_type<E> >::type::type
to_integral(E e) {
return static_cast<typename std::underlying_type<E>::type>(std::move(e));
}
第一次专业化。对于第二个,我建议:
template<typename E>
typename std::enable_if<!std::is_enum<E>::value&&std::is_integral<E>::value,E>::type
to_integral(E e) {
return std::move(e);
}
应该在MSVC2012 live example中有效。注意额外条件和std::move
(以防你有一个bigint类符合is_integral)。 (通常允许在std
)中专门化这些特征。这也意味着如果你打电话给to_integral(3.14)
,你会收到错误,我认为这是错误的。
哦,template<bool b, class T=void>using enable_if_t=typename std::enable_if<b,T>::type;
可以节省大量typename
垃圾邮件(但是,2012年要么缺乏支持,2013年对这种事情有支持)。
答案 1 :(得分:0)
这是我用struct
将其包裹起来的实际情况,VS2012很满意。我怀疑它是最聪明的实现,但它适用于我的测试用例。如果其他人提交了更好的东西,我会高兴地接受它!此外,我还对@ Yakk使用std::move
的想法进行了抨击。
在clang/GCC和VS2013中工作,找不到在线VS2012编译器,但它在本地工作。
#include <type_traits>
#include <cstdio>
template<class E, class Enable = void>
struct to_integral_helper
{
static E inner(E e)
{
return std::move(e);
}
};
template<typename E>
struct to_integral_helper<E, typename std::enable_if<std::is_enum<E>::value>::type>
{
static typename std::underlying_type<E>::type inner(E e)
{
return static_cast<typename std::underlying_type<E>::type>(std::move(e));
}
};
template<typename E>
auto to_integral(E e) -> decltype(to_integral_helper<E>::inner(e))
{
return to_integral_helper<E>::inner(std::move(e));
}
enum class MyEnum { A = 5 };
int main()
{
auto a = to_integral(42);
auto b = to_integral(MyEnum::A);
printf("%d\n", a);
printf("%d\n", b);
}