在VS2012中使用函数签名中的enable_if和underlying_type

时间:2014-12-01 15:59:43

标签: c++ visual-studio-2012 sfinae enable-if

此代码适用于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出现在函数签名中,编译器往往不喜欢非枚举类型。

2 个答案:

答案 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/GCCVS2013中工作,找不到在线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);
}