Мне нужен шаблонный класс, который, учитывая кортеж и используемый в качестве базового класса, обеспечивает метод с поведением по умолчанию для каждого типа элемента в кортеже. Эти методы должны быть виртуальными, чтобы их можно было переопределить в наследующем классе.
Код, приведенный ниже, делает именно это:
#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_t\n"; }
};
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_t\n";
}
};
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_t\n"; }
| ^
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_t\n"; }
| ~~~~~~~~~^
Я предположил, что когда дело доходит до шаблонов, нет реальной разницы, если тип извлекается из константы или переменной.
Код начинает работать после изменения в строке ниже (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).
Я хотел бы услышать любую идею, почему это не получится. Заранее спасибо.