在模板基类中为继承类中的可选重写生成虚拟方法

Generating virtual methods in template base class for optional override in inheriting class

本文关键字:重写 方法 虚拟 基类 继承      更新时间:2023-10-16

我想要的是一个模板类,给定元组并用作基类,为元组中的每种元素类型提供具有默认行为的方法。这些方法应该是虚拟的,这样它们就可以在继承类中被重写。

下面的代码正是这样做的:

#include <tuple>
#include <iostream>
struct A {};
struct B {};
struct C {};
template <typename Class, uint16_t tag>
struct def {
using message_type = Class;
static constexpr uint16_t class_tag = tag;
};
// (3) adding "constexpr" or "const" causes compilation failure
auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
template <typename T> // (1)
struct base_handler_t {
virtual void h(T const& t) { std::cout << "base_handler_tn"; }
};
template <typename ...Ts> // (2) - adding "const" to "std::tuple<Ts...>" in line below makes code work again if "t" is constant
struct base_handler_t<std::tuple<Ts...>> : public base_handler_t<typename Ts::message_type>...{
using base_handler_t<typename Ts::message_type>::h...;
};
struct service_t : public base_handler_t<decltype(t)> {
using base_handler_t<decltype(t)>::h;
void h(B const & b) {
std::cout << "service_tn";
}
};
int main() {
service_t n;
n.h(A());
n.h(B());
}

在找到破坏代码的精确且最小的示例后进行编辑:

按原样输入时,上面的代码可以正常工作,但如果注释(3)(关于将constexpr添加到t的定义(下面的行更改为:

const auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());

constexpr auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());

代码编译失败。编译器声称:

x.cc: In function ‘int main()’:
x.cc:35:16: error: no matching function for call to ‘service_t::h(A)’
35 |         n.h(A());
|                ^
x.cc:28:14: note: candidate: ‘void service_t::h(const B&)’
28 |         void h(B const & b) {
|              ^
x.cc:28:26: note:   no known conversion for argument 1 from ‘A’ to ‘const B&’
28 |         void h(B const & b) {
|                ~~~~~~~~~~^
x.cc:18:22: note: candidate: ‘void base_handler_t<T>::h(const T&) [with T = const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >]’
18 |         virtual void h(T const& t) { std::cout << "base_handler_tn"; }
|                      ^
x.cc:18:33: note:   no known conversion for argument 1 from ‘A’ to ‘const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >&’
18 |         virtual void h(T const& t) { std::cout << "base_handler_tn"; }
|                        ~~~~~~~~~^

我假设,当涉及到模板时,如果类型是从常量或变量中提取的,就没有真正的区别。

代码在以下行更改后开始工作(2(:

struct base_handler_t<std::tuple<Ts...>> : ...

struct base_handler_t<const std::tuple<Ts...>> : ...

为什么会这样?是因为std::tuple<Ts...>const std::tuple<Ts...>不完全匹配吗?本案的具体规则是什么?

提前谢谢。

最初的求助请求:

在原始代码中,每种类型的方法(上例中的A、B和C(都是在"service_t"类中手工定义的。我试图像上面的例子一样解决这个问题。只要所有方法都存在,代码仍然可以正常工作。当我注释掉单个方法时,我收到一个错误,并没有匹配的方法可以调用,然后是可能匹配的列表。方法与参数std::tuple<def<....等匹配——在我上面的代码片段中,似乎一切都很好(因此为每个元组元素类型生成一个方法(,但有的东西阻止了更大的代码库与模板(2(匹配,而是使用模板(1(。

我想听听任何为什么会失败的想法。提前谢谢。

使用

auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());

decltype(t)std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>

如果tconst限定的:const auto t = /*..*/,则decltype(t)const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>

所以对于base_handler_t<decltype(t)>base_handler_t<const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>>只匹配主模板定义,而不是您的专业化。

您可以使用base_handler_t<std::remove_const_t<decltype(t)>>base_handler_t<std::decay_t<decltype(t)>>(删除引用,然后删除cv限定符(