【问题标题】:How to achieve implicit conversion from enum value to class template如何实现枚举值到类模板的隐式转换
【发布时间】:2015-10-25 06:35:09
【问题描述】:

假设我有许多枚举 enum EnumA { fooA, barA, quuzA };enum EnumB {fooB, barB, quuzB }; 等,以及一些模板结构的形式:

template<EnumA value> struct StructA {};
template<EnumB value> struct StructB {};

等等等等。我想实现从枚举值到相应结构的隐式转换,显然当枚举值是编译时间常数时。

对于更多上下文,我希望能够在如下上下文中使用它。我有一个功能

template<typename T> void somefunc(int somepar, T);

我可以将其用作somefunc(somepar, StructB&lt;quuzB&gt;());,并且我希望能够将其用作somefunc(somepar, quuzB);

(不,将 somefunc 重载为一组模板函数,每个枚举一个,执行适当的结构包装本身不是一种选择,因为(1)枚举的数量和(2)我还希望有类似template&lt;typename T1, typename T2&gt; void somefunc(int par1, T1, int par2, T2) 的东西用作somefunc(p1, StructB&lt;quuzB&gt;(), p2, StructA&lt;barA&gt;()) 等)

【问题讨论】:

  • 如果您的代码与此示例中的代码相同,请注意fooA == fooB(两者都是ints),因此无法解析调用somefunc(42, fooA)。您可以使用enum classes 消除歧义。
  • 啊,好点子。是的,代码是原样的。我希望,考虑到编译器在大多数情况下在区分枚举方面做得很好,它也能够解决这个问题。我猜可能可以使用 C++11 中的枚举类来处理它,但不能使用 C++98 中的普通枚举?
  • 至少在 C++11 enum classes 上理论上是可能的。但我不知道如何实现它。
  • 另外,enum classes 需要命名空间,因此在冗长 8-/ 方面不会有太大收获

标签: c++ enums type-conversion implicit-conversion


【解决方案1】:

您想在调用中作为实际参数提供的enum 值最终作为运行时 值。但是要将其转换为特定于值的类型,需要了解值的编译时。目前,您正在手动提供编译时知识,并且使用您提出的更简单的调用语法,获取该知识的唯一通用方法是通过一些源代码预处理。

为了避免预处理(通过任何方式,例如宏或脚本,或训练有素的黑猩猩),您可以调整调用语法的要求,例如显式提供enum 类型,所以不是

somefunc( somepar, quuzB );

……你会写

somefunc<EnumB, quuzB>( somepar );

例如,宏仅用于说明,以防万一,但我建议不使用宏(显式好,隐式不好):

enum EnumA { fooA, barA, quuzA };
enum EnumB { fooB, barB, quuzB };

template< EnumA value> struct StructA {};
template< EnumB value> struct StructB {};

//template<typename T> void somefunc( int somepar, T );
template< class Enum_type, Enum_type value >
void somefunc( int somepar );

void foo()
{
    somefunc<EnumB, quuzB>( 42 );
}

//-------------------------------------------------------------

#define SOMEFUNC( par, e ) somefunc<decltype(e), e>( par )

void bar()
{
    SOMEFUNC( 42, quuzB );
}

//-------------------------------------------------------------

#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;

#ifdef __GNUC__
#   include <cxxabi.h>
    auto demangled( char const name[] )
        -> string
    {
        int status = 0;
        char* c_result = abi::__cxa_demangle( name, 0, 0, &status);
        string result = c_result;
        free( c_result );
        return result;
    }
#else
    auto demangled( char const name[] )
        -> string
    { return name; }
#endif

template< class Type >
void somefunc_impl( int x, Type )
{
    cout << x << ", " << demangled( typeid( Type ).name() ) << endl;
}

template< class Enum_type >
struct Enum_to_struct;

template<>
struct Enum_to_struct<EnumA>
{
    template< EnumA e > struct Struct{ using T = StructA<e>; };
};

template<>
struct Enum_to_struct<EnumB>
{
    template< EnumB e > struct Struct{ using T = StructB<e>; };
};

template< class Enum_type, Enum_type value >
void somefunc( int somepar )
{
    using Struct = typename Enum_to_struct<Enum_type>::template Struct<value>::T;
    somefunc_impl( somepar, Struct() );
}

auto main() -> int
{
    foo();
    bar();
}

这可以简化——您可以避免 Enum_to_struct 特征——通过使用部分专用的类模板而不是不相关的单独命名的模板,如 StructAStructB

enum EnumA { fooA, barA, quuzA };
enum EnumB { fooB, barB, quuzB };

template< class Enum_type, Enum_type value >
struct Struct{};

template< EnumA e >
struct Struct< EnumA, e > {}; // Whatever, corresponding to StructA

template< class Enum_type, Enum_type value >
void somefunc( int somepar );

void foo()
{
    somefunc<EnumB, quuzB>( 42 );
}

//-------------------------------------------------------------

#define SOMEFUNC( par, e ) somefunc<decltype(e), e>( par )

void bar()
{
    SOMEFUNC( 42, quuzB );
}

//-------------------------------------------------------------

#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;

#ifdef __GNUC__
#   include <cxxabi.h>
    auto demangled( char const name[] )
        -> string
    {
        int status = 0;
        char* c_result = abi::__cxa_demangle( name, 0, 0, &status);
        string result = c_result;
        free( c_result );
        return result;
    }
#else
    auto demangled( char const name[] )
        -> string
    { return name; }
#endif

template< class Type >
void somefunc_impl( int x, Type )
{
    cout << x << ", " << demangled( typeid( Type ).name() ) << endl;
}

template< class Enum_type, Enum_type value >
void somefunc( int somepar )
{
    somefunc_impl( somepar, Struct<Enum_type, value>() );
}

auto main() -> int
{
    foo();
    bar();
}

为了完整起见,即使宏是 Evil™ 并且我建议不要使用它们,如果您选择使用宏路线,您可以将开销减少到 单个宏,而不是每个函数一个,在不太明显的调用语法的代价:

#define TV( v ) decltype( v ), v

void qwepoi()
{
    somefunc<TV( quuzB )>( 42 );
}

【讨论】:

  • 考虑到重点是为somefunc 的用户提供更好、更简单的语法,并考虑到,正如您所提到的,参数值(即使在调用时知道编译时间place) 是函数的运行时,恐怕没有预处理就不可能使用“好”语法的解决方案,所以我不得不这样做。
  • (续)您提出的其他解决方案与我已经放弃的解决方案非常相似,因为语法不能很好地扩展到多个参数对的情况(比较 somefunc(42, quuzB, 32, barA)somefunc&lt;EnumB, quuzB, EnumA, barA&gt;(42, 32) )。我真的很感激我的努力,但在这种情况下,我似乎无法逃避宏方法来实现良好的语法。仍然非常感谢您从本质上确认无法完成我想要的事情;-)
猜你喜欢
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多