Spi*_*ire 5 c++ templates dispatch
我正在重构一个大类 - 让我们称之为Big
- 它有大量的复制粘贴代码.这些复制粘贴代码中switch
case
的大部分存在于s中,其中只涉及的类型最终不同.代码基于enum
类的成员变量进行切换,其值仅在运行时已知.
我尝试解决这个问题涉及到一个Dispatcher
类,它通过一个static
名为的函数查找适当类型的函数lookup()
.始终调用执行实际工作的函数,go()
并且必须在包装器类模板中定义它们(其唯一参数是enum
当前正在打开的运行时值).这些go()
功能可能是也可能不是模板功能本身.
这是代码的提炼版本.我对这个问题表示道歉,但这一点很短,我没有失去重要的背景.
#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));
}
Run Code Online (Sandbox Code Playgroud)
这主要是有效的,除了:
lookup()
.lookup()
为我们想要支持的每个新的函数模板参数编写一个新的函数模板重载go()
.以下是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>;
^
Run Code Online (Sandbox Code Playgroud)
我的问题是双重的:
代码必须在Visual C++ 9(2008)下可编译,因此我不能使用C++ 11特定的任何东西.
由于go
是模板的从属名称,因此您需要使用template
消除歧义器:
case a: return &FunctionWrapper<a>::template go<T>;
// ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
// ^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
这告诉编译器解析::
作为模板名称的作用域解析operator()后面的内容,以及后续的尖括号作为模板参数的分隔符.
为什么这不能在GCC下构建,我该如何解决?
因为GCC是符合标准,并进行两阶段的名称查找,而MSVC延迟名称查找,直到实例化时,因此,知道go
是一个模板的名称.
在实例化之前,这些信息不可用,因为不可能知道什么T
是,并且主模板可以专用于给定的,T
因此go
不是成员函数模板的名称,而是数据成员的名称.
这就是说,我希望MSVC template
无论如何都支持消歧器,所以添加它应该使你的程序在GCC/Clang/what-conforms-to-the-Standard和MSVC上编译.