Using enable_if and underlying_type in function signature in VS2012

邮差的信 提交于 2019-12-13 03:55:34

问题


This code works in VS2013 and other compilers (tested clang 3.4 and gcc 4.8) but fails to compile in 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);
}

How can I write to_integral in VS2012? Is it possible? I tried using enable_if on the return argument and as a parameter but then the underlying_type appears in the function signature which compilers tend not to like for non-enum types.


回答1:


Put the enable_if in the return type:

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));
}

or the simpler:

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));
}

for the first specialization. For the second, I'd recommend:

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);
}

should work in MSVC2012 live example. Note the extra condition, and the std::move (just in case you have a bigint class that qualifies as is_integral). (it is usually allowed to specialize such traits in std). It also means that if you call to_integral(3.14) you get an error, which I think is good.

Oh, and template<bool b, class T=void>using enable_if_t=typename std::enable_if<b,T>::type; can save a lot of typename spam (however, 2012 either has lack of support, and 2013 has flaky support, for this kind of thing).




回答2:


Here's my stab at wrapping it up in a struct, which VS2012 is happy with. I doubt it's the smartest implementation but it's working for my test case. If someone else submits something nicer though I'll happily accept it! Also I nicked @Yakk's idea of using std::move.

Working in clang/GCC and VS2013, and can't find an online VS2012 compiler but it's working locally.

#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);
}


来源:https://stackoverflow.com/questions/27232224/using-enable-if-and-underlying-type-in-function-signature-in-vs2012

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!