Про шаблонные конструкторы нужно помнить как минимум две вещи:
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 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++... Остается только горевать.