#c #c 11 #templates
Вопрос:
Допустим, у меня есть этот класс:
template <class T>
class Test
{
Test(T* x);
const T* const t;
int i{0};
};
Я хочу t
, чтобы меня всегда инициализировали с x
:
template <class T> Test<T>::Test(T* x) : t{x} {}
И у меня есть две специализации:
template <> Test<Foo>::Test(Foo* x) : t{x} { i = 1; }
template <> Test<Bar>::Test(Bar* x) : t{x} { i = 2; }
Затем я расширяю класс некоторыми другими вещами, и этот первый (шаблонный) конструктор делает гораздо больше, чем просто настройка t
.
Все, что я хочу сделать для обоих T = Foo
и T = Bar
.
Есть ли какой-то способ, которым я могу вызвать шаблонный конструктор из специализированных конструкторов?
//This does not work, since it will create a delegation cycle
template <> Test<Foo>::Test(Foo* x) : Test(x) { i = 1; }
template <> Test<Bar>::Test(Bar* x) : Test(x) { i = 2; }
Ответ №1:
Для этого вы можете использовать делегирующий конструктор.
Вы можете создать частный конструктор, который принимает указатель на t
и int
для i
. Затем вы можете использовать это для установки x
и i
и запуска всего общего кода.
Это выглядело бы так:
template <class T>
class Test
{
public:
Test(T* x) : Test(x, 0) { /*code for default case, runs after delegate*/ }
private:
Test(T* t, int i) : t(t), i(i) { /*code to run for everything*/ }
const T* const t;
int i;
};
template <> Test<Foo>::Test(Foo* x) : Test(x, 1) { /*code only for Foo, runs after delegate*/ }
template <> Test<Foo>::Test(Bar* x) : Test(x, 2) { /*code only for Bar, runs after delegate*/ }
Может ли конструктор делегата быть универсальным/шаблонным конструктором (с той же подписью, что и конкретные специализированные конструкторы для Foo и Bar)?
Нет, это невозможно. Когда вы специализируете шаблон функции, вы не создаете новую функцию, а вместо этого указываете, что если T
выводится к типу, указанному в специализации, то используйте определение специализации вместо общего.
Вот почему у меня есть вызов «все три конструктора» (универсальный и две специализации) Test(T* t, int i)
, который обрабатывает код, общий для всех случаев.
Комментарии:
1. В качестве альтернативы можно использовать фиктивный аргумент (по аналогии с std::nothrow , который имеет пустой тип и используется только для выбора определенной перегрузки).
2. Итак, как мне бы хотелось, чтобы конструктор делегата был универсальным/шаблонным конструктором (и имел ту же подпись, что и конкретные специализированные конструкторы для Foo и Bar)… Это невозможно?
3. @Ти Джей Эверс Нет, это невозможно. Когда вы специализируете шаблон функции, вы не создаете новую функцию, а вместо этого указываете, что если
T
выводится тип, который вы выделяете в специализации, то используйте определение специализации вместо общего. Вот почему у меня есть все «три», универсальные и две специализации, вызов конструкторовTest(T* t, int i)
, которые обрабатывают код для всех случаев.
Ответ №2:
вы думали о наследовании? Первое, что приходит мне в голову, — это создать Test
класс, производный от базового класса, в котором будут все вещи, которые вы хотели, чтобы о них позаботились и фу, и бар. Таким образом, вы можете вызвать конструктор базового класса внутри производного класса (тест), а затем просто выполнить действия для Foo
и bar
.