Почему конструкторы не могут выводить аргументы шаблона?
template< class T >
class Foo {
public:
Foo( T t ) { }
};
int main () {
int i = 0;
Foo f( i );
}
В приведенном выше коде компилятор жалуется, что аргументы шаблона отсутствуют до 'f'. Я понимаю, что вывод аргументов шаблона для класса из аргументов конструктору не является частью стандарта, но почему мой вопрос? Разве компилятор не имеет всей необходимой информации, чтобы неявно создать экземпляр Foo<int>
и вызвать его конструктор?
Отредактировано, чтобы было ясно, что я вызываю конструктор с int
(в отличие от short
, long
, void*
и т.д.)
Ответы
Ответ 1
Потому что никто не указал, как именно это работает. В настоящее время для стандартного комитета есть текущее предложение, чтобы оно работало. В нем также перечислены некоторые из трудностей:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4471.html
Обновление: здесь самая новая версия предложения:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0091r0.html
Ответ 2
TL; DR: Специализация шаблона
Они могут, но только аргументы шаблона самой функции, а не типа.
Объявление Foo f(0);
является незаконным, потому что нет имени с именем Foo
. Возможно, вы думали о
auto f = Foo(0);
но это тоже недопустимо, потому что компилятор не знает, в какую область действия искать (существуют бесконечные потенциальные типы с конструктором с именем Foo
и со специализацией, возможно, более одного с конструктором Foo(int)
)
Обычный способ сделать это с помощью вспомогательной функции factory:
auto f = make_foo(0);
где тип возврата функции factory зависит от типа вывода его параметров.
Вы можете себе представить, что функции factory могут быть автоматически сгенерированы в области пространства имен, а затем применяются обычные правила перегрузки функций, но это сталкивается с большими трудностями, потому что могут быть аргументы шаблона как для самого типа, так и для самого конструктора. Они могут быть просто объединены с ограничением на то, что это исключает шаблоны классов с вариационными списками аргументов, потому что не будет возможности различать, где начинаются параметры типа и параметры функции.
Ответ 3
Foo
- это шаблон класса, а не класс. Его тип всегда должен быть каким-то образом поставлен для класса, который должен быть сгенерирован с правильными типами. Вы не можете сделать Foo
, потому что Foo
не является типом, но Foo<int>
is. Он создает класс следующим образом:
class Foo {
public:
Foo( int t ) { }
};
Если вы предоставили только Foo
, компилятор не знал бы, как создать класс. Foo<int> f(0)
работает, потому что Foo<int>
генерирует класс, заменяя T
на int
. По типу, который вы вызываете конструктор, компилятор уже знает, что конструктор принимает int
.
Ответ 4
Имя класса, который вы хотите создать, это
Foo<int>
(как вы указываете).
Можно также написать Foo<short> f(0)
или Foo<unsigned long> f(0)
,
или Foo<Foo<double>*> f(0)
. В этих случаях, однако, вы не ожидаете, что компилятор сможет угадать тип, если вы пишете только Foo f(0)
.
Можно предположить, что С++ мог быть указан с правилами, чтобы сделать
некоторые такие догадки определенным образом (например, буквальный 0
подразумевает параметр типа int
и ни один другой), но тогда язык будет еще более
сложнее, чем сейчас, и были бы дополнительные пути для
люди делают ошибки программирования.
Фактически записывая то, что вы имеете в виду в декларации, подобной этой
кажется, не слишком много, чтобы спросить.
Изменить: После публикации этого вопроса я заметил в другом ответе, что есть
предложение сделать такую особенность С++, действительно, как можно было себе представить.