gpt4 book ai didi

带有模板参数的 C++ 函数分派(dispatch)

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:25:15 24 4
gpt4 key购买 nike

我正在重构一个大类——我们称它为 Big——它有大量的复制粘贴代码。大部分复制粘贴代码都存在于 switch case 中,其中只有涉及的类型最终有所不同。代码根据类的 enum 成员变量进行切换,该类的值仅在运行时才知道。

我试图解决这个问题涉及到有一个 Dispatcher 类,它通过一个名为 lookup()static 函数查找适当类型的函数。执行实际工作的函数总是称为 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));
}

这主要是有效的,除了:

  1. 它在 Visual C++ 9 (2008) 下构建并运行良好,但在 GCC 4.8 下,它会导致 lookup() 的函数模板重载出现编译错误。
  2. 它要求为我们希望在 go() 中支持的每个新函数模板参数编写一个新的 lookup() 函数模板重载。
  3. 使用起来既麻烦又困惑。

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>;
^

我的问题有两个:

  1. 为什么在 GCC 下无法构建,我该如何解决?
  2. 是否有更好(即不那么麻烦和困惑)的方法来做到这一点?

代码必须可以在 Visual C++ 9 (2008) 下编译,所以我不能使用任何特定于 C++11 的东西。

最佳答案

由于 go 是模板的从属名称,因此您需要使用 template 消歧器:

case a: return &FunctionWrapper<a>::template go<T>;
// ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
// ^^^^^^^^

这告诉编译器将范围解析运算符 (::) 后面的内容解析为模板的名称,并将随后的尖括号解析为模板参数的分隔符。

Why is this failing to build under GCC, and how do I fix it?

因为 GCC 符合标准,并且执行 two-phase name lookup ,而 MSVC 将名称查找延迟到实例化时间,因此知道 go 是模板的名称。

在实例化之前,此信息不可用,因为不可能知道 T 是什么,并且主模板可以专门用于给定的 T 以便 go 不是成员函数模板的名称,而是数据成员的名称。

这就是说,我希望 MSVC 无论如何都支持 template 消歧器,因此添加它应该可以使您的程序在 GCC/Clang/whatever-conforms-to-the-Standard 和 MSVC 上都能编译。

关于带有模板参数的 C++ 函数分派(dispatch),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16552166/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com