У меня есть класс wrapper
, который принимает template<typename> class
в качестве параметра. Однако у меня есть класс с дополнительными параметрами, например template<typename T, typename P> Special
, и я хотел бы вписать его в параметр класса-оболочки.
Единственное решение, которое я могу найти, выглядит следующим образом:
// test.cpp
// Base class
class Base
{
public:
int data_base = 1;
};
// A derive class
template<typename P>
class Derived : public Base
{
P data_child;
};
// A wrapper to accept any type similar to Derived<P> as the base class
template<template<typename> class BT, typename P>
class Wrapper : public BT<P>
{
int do_something;
};
// However, a special derived class need an extra template parameter
template<typename T, typename P>
class Special : public Derived<P>
{
T special;
};
// Try to fit Special into the type of BT of Wrapper
template<typename T>
class SpecialBridge
{
public:
template<typename P>
using BT = Special<T, P>;
};
// actually define the type to use (Error!)
template<typename T, typename P>
using Actual = Wrapper<SpecialBridge<T>::BT, P>;
int main()
{
Actual<int, int> obj;
return obj.data_base;
}
Однако и C++17, и C++20 утверждают одно и то же. Компилятор не может вычесть тип SpecialBridge<T>::BT
$ g++ -std=c++17 test.cpp -o test
test.cpp:40:47: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class> class BT, class P> class Wrapper’
40 | using Actual = Wrapper<SpecialBridge<T>::BT, P>;
| ^
test.cpp:40:47: note: expected a class template, got ‘SpecialBridge<T>::BT’
test.cpp: In function ‘int main()’:
test.cpp:44:3: error: ‘Actual’ was not declared in this scope
44 | Actual<int, int> obj;
| ^~~~~~
test.cpp:44:10: error: expected primary-expression before ‘int’
44 | Actual<int, int> obj;
| ^~~
test.cpp:45:10: error: ‘obj’ was not declared in this scope
45 | return obj.data_base;
| ^~~
Есть ли способ сделать это правильно? Извините за нестандартный способ описания проблемы. Пример кода более или менее понятен.
template<typename...>
. Или даже вот это демо
Wrapper
предоставляет параметр P
для BT<P>. Когда используется Special
, я надеюсь получить что-то вроде Wrapper<Special<T>, P>
, где T
идет непосредственно с Special
, а P
предоставляется в момент фактического использования. Возможно ли это с typename...
? Пожалуйста, уточните.
Вы можете изменить обертку на template<template<typename...> class BT, typename... P>class Wrapper : public BT<P...> {};
. Тогда можешь просто написать Wrapper<Special, int, int> w;
Большинство производных классов имеют один параметр P
, только специальному классу нужны два параметра. Код в классе Wrapper
должен использоваться всеми производными или специальными классами. (Мне не нужно несколько классов-оболочек). Рабочая демонстрация требует, чтобы все производные и специальные классы имели два параметра!
Вы можете ограничить количество параметров максимум двумя. Кроме того, предоставленный мной код также будет работать для другого производного класса с одним P
.
Я понимаю. Кажется, это работает. Мне нужно вернуться к реальному проекту и посмотреть, решит ли он проблему. Хотите нарисовать простой ответ. Я обязательно приму это, как только будет доказано, что это работает. Большое спасибо!
Я добавил это как ответ. Обратите внимание на использование requires(sizeof...(P)<=2)
для проверки того, что мы не передаем более двух параметров в P
. Вы можете удалить его, если он вам не нужен. Изменить его на код С++ 11 тривиально.
Вы можете использовать пакеты параметров, как показано ниже:
template<template<typename...> class BT, typename... P> requires(sizeof...(P)<=2)
class Wrapper: public BT<P...>
{
int do_something;
};
int main() {
Wrapper<Special, int, int> w;
return w.data_base;
}
Почему отрицательные голоса? В ответе нет ничего плохого. Ответ полезен, а кнопка «против» предназначена для бесполезных ответов.
Из сообщения об ошибке:
note: expected a class template, got 'SpecialBridge<T>::BT'
ясно, что компилятор не смог понять, что SpecialBridge<T>::BT
— это шаблон в определенном вами псевдониме.
Следовательно, ключевое слово шаблона необходимо, поскольку BT
— зависимое имя (оно зависит от параметра шаблона T
).
template<typename T, typename P>
using Actual = Wrapper<SpecialBridge<T>::template BT, P>;
// ^^^^^^^^^ ---> add this!
Теперь ваш код будет работать как положено: Посмотрите демо-версию
@Downvoter Было бы неплохо узнать, в чем причина отрицательного голосования.
Почему бы просто не изменить первый параметр оболочки на
template<template<typename...> class BT
?