如何实现从枚举值到类模板的隐式转换

时间:2015-10-25 06:35:10

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

假设我有许多枚举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<quuzB>());,我希望能够将其用作somefunc(somepar, quuzB);

(并且,不,将somefunc重载为一组模板函数,每个枚举一个,执行适当的结构包装本身不是一个选项,因为(1)枚举的数量和(2)我的事实我还希望将template<typename T1, typename T2> void somefunc(int par1, T1, int par2, T2)用作somefunc(p1, StructB<quuzB>(), p2, StructA<barA>())等等。)

1 个答案:

答案 0 :(得分:2)

您希望在通话中作为语法参数提供的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 );
}