Почему конструкторы не могут выводить аргументы шаблона?

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 и ни один другой), но тогда язык будет еще более сложнее, чем сейчас, и были бы дополнительные пути для люди делают ошибки программирования. Фактически записывая то, что вы имеете в виду в декларации, подобной этой кажется, не слишком много, чтобы спросить.

Изменить: После публикации этого вопроса я заметил в другом ответе, что есть предложение сделать такую ​​особенность С++, действительно, как можно было себе представить.