пятница, 12 ноября 2010 г.

Шаблонные конструкторы против обычных

Про шаблонные конструкторы нужно помнить как минимум две вещи:

1) если он не имеет аргументов, то его нельзя вызвать или специализировать (синтаксис не позволяет)
2) он "сильнее" конструктора по умолчанию, но "слабее" copy- и move- конструкторов

Если мы в своем классе Foo объявили шаблонный конструктор без параметров, то происходят следующие вещи:

struct Foo {
   int a;
   template<class T> Foo() { a = 1; }
};

/* 1 */ Foo f;

Во-первых, конструктор по умолчанию теперь не генерируется. Т.е. на строке 1 компилятор выдаст сообщение об ошибке. В этом смысле шаблонный конструктор сильнее дефолтного.

Во-вторых, наш шаблонный конструктор мы вызвать не сможем, ибо синтаксис не позволяет нам писать параметр T возле имени конструктора:

Foo<T> f1; /* тут шаблонным стал класс, а не конструктор */

Foo pf = new Foo<int>(); /* тут то же самое, хотя такую запись можно было бы и разрешить: в левой части нет параметра T, значит, это не шаблонный класс, а конструктор */

Foo f2<T>; /* а так писать вообще нельзя :) */ 

/* у Вас есть ещё идеи? */

В-третьих, мы не можем написать специализацию для шаблонного конструктора без аргументов:

template<> Foo::Foo<int>() { a = 3; }

Строка выше синтаксически некорректна: нельзя писать угловые скобки после имени конструктора (они воспринимаются как параметры шаблонного класса, но у нас нет никакого шаблонного класса, только конструктор).

Итак, шаблонный конструктор можно специализировать и вызвать только если у него есть аргументы, тогда будет работать механизм type inference. Наиболее интересен случай одного аргумента:

struct Bar {
   int a;
   template<class T> Bar(T) { cout << "ок"; }
};

/* 1 */ Bar b1(5);
/* 2 */ Bar pb = new Bar('a');
/* 3 */ Bar b2(b1);

В строках 1 и 2 на экран будет выведено "ок". Вопрос: что будет в строке 3? Ответ: на экране ничего нового не появится!
Казалось бы, в качестве T может вполне себе выступать тип const Bar&, т.е. конструктор копирования описывается семейством конструкторов Bar(T t), тогда в строке 3 должен вызываться именно наш шаблонный конструктор. Ан нет!
Язык C++ требует, чтобы конструктор копирования (в C++0x также и move-конструктор) были объявлены явно, а не выведены в результате инстанцировния. Поэтому в нашем примере конструктор копирования будет сгенерирован компилятором.

Нашему горю не поможет даже явная специализация

template<>
Bar::Bar(const Bar &) { cout << "OK"; }

Всё равно конструктор копирования (и move-конструктор в C++0x) будет сгенерирован компилятором.

Обратите внимание на синтаксис специализации: без угловых скобочек. Вариант с угловыми скобочками после имени конструктора Bar<const Bar &>, как мы помним, синтаксически недопустим.
 
Налицо еще одна асимметрия языка C++... Остается только горевать.

3 комментария:

  1. Не нужно горевать. Нужно придумывать новые пути решения ситуации, в которой может понадобиться шаблонный конструктор.
    Похоже, как раз этим мне и придётся заниматься в ближайшее время)

    ОтветитьУдалить
  2. Сколько людей, столько и мнений =) Лично мне нравится шаблонный конструктор.

    ОтветитьУдалить
  3. Goyang Casino Hotel - Las Vegas
    Goyang Casino Hotel is the official name of the property for casino-roll.com its gaming facilities https://sol.edu.kg/ in the resort Las Vegas. The resort's gaming goyangfc.com floor, casino, and spa poormansguidetocasinogambling are

    ОтветитьУдалить