Есть ли способ вызвать конструктор шаблона из специализированного конструктора?

#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 .