C ++函数调度与模板参数 [英] C++ function dispatch with template parameters
问题描述
我正在重构一个大类 - 让我们称之为 Big
- 有大量的复制粘贴代码。这个复制粘贴代码大部分存在于 switch
case
中,只有涉及的类型不同。代码是基于其值只在运行时已知的类的枚举
成员变量进行切换。
我的尝试修复这个问题涉及到一个 Dispatcher
类,通过 static
函数查找相应类型的函数 lookup()
。执行实际工作的函数总是调用 go()
,并且必须在包装类模板中定义(其唯一的参数是运行时枚举
当前正在打开的值)。 go()
函数本身可能是也可能不是模板函数。
这里是一个蒸馏版本的代码。对于这个长度,我的道歉,但这是我能得到它,而不会失去重要的上下文。。
#include< cassert ;
class Big
{
public:
enum RuntimeValue {a,b};
Big(RuntimeValue rv):_rv(rv){}
bool equals(int i1,int i2)
{
return Dispatcher }
template< typename T>
bool isConvertibleTo(int i)
{
return Dispatcher< IsConvertibleTo,bool(int)> :: lookup< T&
}
private:
template< RuntimeValue RV>
struct Equals
{
static bool go(int i1,int i2)
{
//假设这是一些依赖于RV $ b $的复杂代码b //是编译时常量。
return i1 == i2;
}
};
template< RuntimeValue RV>
struct IsConvertibleTo
{
template< typename T>
static bool go(int i)
{
//假设这是一些依赖于RV
//的编译时常量的复杂代码。
return static_cast< T>(i)== i;
}
};
template< template< RuntimeValue>类FunctionWrapper,typename函数>
struct Dispatcher
{
static function * lookup(RuntimeValue rv)
{
switch(rv)
{
case a:return& ; FunctionWrapper< a> :: go;
case b:return& FunctionWrapper< b> :: go;
默认值:assert(false); return 0;
}
}
template< typename T>
static function * lookup(RuntimeValue rv)
{
switch(rv)
{
case a:return& FunctionWrapper< a> :: go< T& ;
case b:return& FunctionWrapper< b> :: go< T>
默认值:assert(false); return 0;
}
}
//根据需要等等...
模板< typename T1,typename T2>
static function * lookup(RuntimeValue rv);
};
RuntimeValue _rv;
};
int main()
{
Big big(Big :: a);
assert(big.equals(3,3));
assert(big.isConvertibleTo< char>(123));
}
这主要工作,除了:
- 它在Visual C ++ 9(2008)下构建和工作良好,但在GCC 4.8下,会导致
lookup )
。 - 它需要为每一个新的函数模板重载
lookup()
我们希望在go()
中支持的新数量的函数模板参数。 - 使用起来很麻烦和混乱。 >
以下是GCC下发生的错误:
Big.cpp:在静态成员函数'static Function * Big :: Dispatcher< FunctionWrapper,Function> :: lookup(Big :: RuntimeValue)':
Big.cpp(66,65):error:预期的主表达式'>'token
case a:return& FunctionWrapper< a> :: go< T>
^
Big.cpp(66,66):错误:期望的主表达式';'token
case a:return& FunctionWrapper< a> :: go< T&
^
Big.cpp(67,65):error:expect>'token之前的主表达式
case b:return& FunctionWrapper< b> :: go< T> ;
^
Big.cpp(67,66):error:期望的主表达式';'token
case b:return& FunctionWrapper< b> :: go< T&
^
我的问题有两个:
- 为什么不能在GCC下构建,我该如何解决?
- It builds and works fine under Visual C++ 9 (2008), but under GCC 4.8 it results in compilation errors in the function-template overload of
lookup()
. - It requires that a new function-template overload of
lookup()
be written for every new number of function template parameters that we want to support ingo()
. - It's cumbersome and confusing to use.
- Why is this failing to build under GCC, and how do I fix it?
- Is there a better (i.e., less cumbersome and confusing) way to do this?
代码必须在Visual C ++ 9(2008)下可编译,所以我不能使用任何C ++ 11特定。
由于 go
的模板,您需要使用模板
消除歧义:
a:return& FunctionWrapper< a> :: template go< T>
// ^^^^^^^^
case b:return& FunctionWrapper< b> :: template go< T>
// ^^^^^^^^
这告诉编译器解析什么遵循范围解析运算符( ::
)作为模板的名称,以及随后的尖括号作为模板参数的定界符。
为什么不能在GCC下构建,我该如何解决?
因为GCC符合标准,并执行两阶段名称查找,而MSVC延迟名称查找直到实例化时间,因此,知道 go
是名称一个模板。
在实例化之前,此信息不可用,因为不可能知道 T
是什么;主模板可以专门用于给定的 T
,以便 go
不是成员函数模板的名称,而不是数据成员。
这说,我希望MSVC支持模板
消歧器,应该使你的程序在GCC / Clang /任何符合标准和MSVC上编译。
I'm in the process of refactoring a large class -- let's call it Big
-- that has a huge amount of copy-paste code. Much of this copy-paste code exists in switch
case
s where only the types involved end up being different. The code is switching based on an enum
member variable of the class whose value is known only at runtime.
My attempt to fix this involves having a Dispatcher
class that looks up appropriately typed functions via a static
function called lookup()
. The functions that do the actual work are always called go()
and have to be defined in a wrapper class template (whose sole parameter is the runtime enum
value currently being switched on). The go()
functions may or may not be template functions themselves.
Here is a distilled version of the code. My apologies for the length, but this was as short as I could get it without losing important context.
#include <cassert>
class Big
{
public:
enum RuntimeValue { a, b };
Big(RuntimeValue rv) : _rv(rv) { }
bool equals(int i1, int i2)
{
return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2);
}
template<typename T>
bool isConvertibleTo(int i)
{
return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i);
}
private:
template<RuntimeValue RV>
struct Equals
{
static bool go(int i1, int i2)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return i1 == i2;
}
};
template<RuntimeValue RV>
struct IsConvertibleTo
{
template<typename T>
static bool go(int i)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return static_cast<T>(i) == i;
}
};
template<template<RuntimeValue> class FunctionWrapper, typename Function>
struct Dispatcher
{
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go;
case b: return &FunctionWrapper<b>::go;
default: assert(false); return 0;
}
}
template<typename T>
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go<T>;
case b: return &FunctionWrapper<b>::go<T>;
default: assert(false); return 0;
}
}
// And so on as needed...
template<typename T1, typename T2>
static Function * lookup(RuntimeValue rv);
};
RuntimeValue _rv;
};
int main()
{
Big big(Big::a);
assert(big.equals(3, 3));
assert(big.isConvertibleTo<char>(123));
}
This mostly works, except that:
Here are the errors that occur under GCC:
Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
Big.cpp(66,65) : error: expected primary-expression before '>' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(66,66) : error: expected primary-expression before ';' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(67,65) : error: expected primary-expression before '>' token
case b: return &FunctionWrapper<b>::go<T>;
^
Big.cpp(67,66) : error: expected primary-expression before ';' token
case b: return &FunctionWrapper<b>::go<T>;
^
My question is twofold:
The code has to be compilable under Visual C++ 9 (2008), so I can't use anything C++11-specific.
Since go
is a dependent name of a template, you need to use the template
disambiguator:
case a: return &FunctionWrapper<a>::template go<T>;
// ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
// ^^^^^^^^
This tells the compiler to parse what follows the scope resolution operator (::
) as the name of a template, and the subsequent angular brackets as delimiters for the template arguments.
Why is this failing to build under GCC, and how do I fix it?
Because GCC is conforming to the Standard, and performs two-phase name lookup, while MSVC delays name lookup until instantiation time and, therefore, knows that go
is the name of a template.
Before instantiation this information is not available, because it is impossible to know what T
is, and the primary template could be specialized for a given T
so that go
is not the name of a member function template, but rather of a data member.
This said, I expect MSVC to support the template
disambiguator anyway, so adding it should make your program compile both on GCC/Clang/whatever-conforms-to-the-Standard and on MSVC.
这篇关于C ++函数调度与模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!