мой вопрос сегодня довольно прост: почему компилятор не может вывести параметры шаблона из конструкторов классов, так же как он может сделать из параметров функции? Например, почему следующий код не может быть действительным:
template<typename obj>
class Variable {
obj data;
public: Variable(obj d)
{
data = d;
}
};
int main()
{
int num = 2;
Variable var(num); //would be equivalent to Variable<int> var(num),
return 0; //but actually a compile error
}
Как я уже сказал, я понимаю, что это неверно, поэтому мой вопрос: почему это не так? Может ли это создать серьезные синтаксические дыры? Есть ли случай, когда эта функция не нужна (когда определение типа вызовет проблемы)? Я просто пытаюсь понять логику, позволяющую вывод шаблонов для функций, но не для должным образом сконструированных классов.
template<class T> Variable<T> make_Variable(T&& p) {return Variable<T>(std::forward<T>(p));}
Ответы:
Я думаю, что это неверно, потому что конструктор не всегда является единственной точкой входа в класс (я говорю о конструкторе копирования и операторе =). Итак, предположим, вы используете свой класс следующим образом:
Я не уверен, будет ли для анализатора настолько очевидным, что тип шаблона является PM MyClass;
Не уверен, что то, что я сказал, имеет смысл, но не стесняйтесь добавлять комментарии, это интересный вопрос.
C ++ 17
Принято, что в C ++ 17 будет вывод типа из аргументов конструктора.
Примеры:
Принятые статьи .
источник
MyClass *pm
здесь было бы недействительно по той же причине, что объявленная функцияtemplate <typename T> void foo();
не может быть вызвана без явной специализации.Вы не можете делать то, о чем просите, по причинам, к которым обратились другие люди, но вы можете сделать это:
что для всех намерений и целей - это то же самое, о чем вы просите. Если вам нравится инкапсуляция, вы можете сделать make_variable статической функцией-членом. Это то, что люди называют именованным конструктором. Таким образом, он не только делает то, что вы хотите, но и почти вызывает то, что вы хотите: компилятор определяет параметр шаблона из (названного) конструктора.
NB: любой разумный компилятор оптимизирует временный объект, когда вы напишете что-то вроде
источник
auto v = make_variable(instance)
так, чтобы вам не приходилось указывать типstatic
члена ... подумайте об этом чуть меньше секунды. Это в стороне: свободные функции замыкающих были действительно решением, но это много избыточных шаблонным, что в то время как вы печатаете это, вы просто знаете , вы не должны , потому что компилятор имеет доступ ко всей информации вы повторить. ... и, к счастью, C ++ 17 канонизирует это.В просвещенную эпоху 2016 года, когда у нас за плечами два новых стандарта, поскольку этот вопрос был задан, и новый не за горами, важно знать, что компиляторы, поддерживающие стандарт C ++ 17, будут компилировать ваш код как есть. .
Вывод шаблона-аргумента для шаблонов классов в C ++ 17
Здесь (любезно предоставлено редакцией принятого ответа Олжасом Жумабеком) документ с подробным описанием соответствующих изменений в стандарте.
Решение проблем, связанных с другими ответами
Текущий самый популярный ответ
Этот ответ указывает на то, что «конструктор копирования и
operator=
» не знает правильных специализаций шаблона.Это чушь, потому что стандартные конструкторы-копии
operator=
существуют только для известного типа шаблона:Здесь, как я отмечал в комментариях, нет причин для того,
MyClass *pm
чтобы быть законным объявлением с новой формой вывода или без нее :MyClass
это не тип (это шаблон), поэтому нет смысла объявлять указатель на типMyClass
. Вот один из возможных способов исправить этот пример:Здесь
pm
он уже имеет правильный тип, поэтому вывод тривиален. Более того, невозможно случайно смешать типы при вызове конструктора копирования:Здесь
pm
будет указатель на копиюm
. ЗдесьMyClass
создается копия,m
которая имеет типMyClass<string>
(а не несуществующий типMyClass
). Таким образом, в точке , гдеpm
выводится «S типа, там есть достаточно информации , чтобы знать , что шаблонный типm
, и , следовательно , шаблон-типаpm
, являетсяstring
.Более того, следующее всегда вызывает ошибку компиляции :
Это потому, что объявление конструктора копирования не является шаблоном:
Здесь шаблон типа копирования-конструктор аргумент соответствует шаблону типа класса в целом; то есть, когда
MyClass<string>
создается экземпляр,MyClass<string>::MyClass(const MyClass<string>&);
создается с ним, а когдаMyClass<int>
создается экземпляр,MyClass<int>::MyClass(const MyClass<int>&);
создается экземпляр. Если это не указано явно или не объявлен шаблонный конструктор, компилятор не имеет причин для создания экземпляраMyClass<int>::MyClass(const MyClass<string>&);
, что, очевидно, было бы неуместным.Ответ Кэтэлина Питиша
Пити приводит пример вывода,
Variable<int>
аVariable<double>
затем заявляет:Как отмечалось в предыдущем примере,
Variable
само по себе не является именем типа, хотя новая функция делает его синтаксически похожим на имя типа.Затем Пити спрашивает, что произойдет, если не будет задан конструктор, который позволил бы сделать соответствующий вывод. Ответ состоит в том, что логический вывод не разрешен, потому что вывод запускается вызовом конструктора . Без вызова конструктора нет вывода .
Это похоже на вопрос о том, какая версия
foo
выведена здесь:Ответ заключается в том, что этот код является незаконным по указанной причине.
Ответ MSalter
Насколько я могу судить, это единственный ответ, который вызывает законное беспокойство по поводу предлагаемой функции.
Пример такой:
Ключевой вопрос заключается в том, выбирает ли компилятор здесь конструктор с выводом типа или конструктор копирования ?
Пробуя код, мы видим, что выбран конструктор копирования. Чтобы развернуть пример :
Я не уверен, как это указывается в предложении и новой версии стандарта; Похоже, что это определяется «руководящими принципами дедукции», которые представляют собой новый стандарт, который я еще не понимаю.
Я также не уверен, почему
var4
удержание незаконно; ошибка компилятора из g ++, похоже, указывает на то, что оператор анализируется как объявление функции.источник
var4
это просто случай "самого неприятного разбора" (не имеющего отношения к выводу аргументов шаблона). Раньше мы просто использовали для этого дополнительные скобки, но в наши дни я думаю, что использование фигурных скобок для однозначного обозначения конструкции - обычный совет.Variable var4(Variable(num));
это рассматривается как объявление функции? Если да, то почемуVariable(num)
это действительная спецификация параметра?По-прежнему отсутствует: это делает следующий код довольно неоднозначным:
источник
Предположим, что компилятор поддерживает то, что вы просили. Тогда этот код действителен:
Теперь у меня есть одно и то же имя типа (Variable) в коде для двух разных типов (Variable и Variable). С моей субъективной точки зрения, это очень сильно влияет на читабельность кода. Наличие одного и того же имени типа для двух разных типов в одном пространстве имен меня вводит в заблуждение.
Позднее обновление: еще одна вещь, на которую следует обратить внимание: частичная (или полная) специализация шаблона.
Что, если я специализируюсь на переменных и не предоставлю конструктора, как вы ожидаете?
Итак, у меня было бы:
Тогда у меня есть код:
Что должен делать компилятор? Использовать определение универсального класса Variable, чтобы сделать вывод, что это Variable, а затем обнаружить, что Variable не предоставляет один конструктор параметров?
источник
Стандарт C ++ 03 и C ++ 11 не позволяет выводить аргумент шаблона из параметров, переданных в конструктор.
Но есть предложение по «Вычету параметров шаблона для конструкторов», так что вы можете вскоре получить то, о чем просите. Изменить: действительно, эта функция была подтверждена для C ++ 17.
См .: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html и http://www.open-std.org/jtc1/sc22/wg21/docs/. документы / 2015 / p0091r0.html
источник
Многие классы не зависят от параметров конструктора. Есть только несколько классов, которые имеют только один конструктор и параметризуются на основе типа (ов) этого конструктора.
Если вам действительно нужен вывод шаблона, используйте вспомогательную функцию:
источник
Выведение типов в текущем C ++ ограничено шаблонными функциями, но уже давно стало понятно, что вывод типов в других контекстах был бы очень полезен. Следовательно, C ++ 0x
auto
.Хотя именно то , что вы предлагаете, будет невозможно в C ++ 0x, следующее показывает, что вы можете довольно близко подойти:
источник
Вы правы, компилятор может легко догадаться, но, насколько мне известно, этого нет в стандарте или C ++ 0x, поэтому вам придется подождать еще как минимум 10 лет (фиксированная скорость оборачиваемости стандартов ISO), прежде чем поставщики компиляторов добавят эту функцию
источник
Давайте посмотрим на проблему применительно к классу, с которым все должны быть знакомы - std :: vector.
Во-первых, очень распространенное использование вектора - использование конструктора, который не принимает параметров:
В этом случае, очевидно, невозможно сделать вывод.
Второе распространенное использование - создание вектора предварительно заданного размера:
Здесь, если был использован вывод:
мы получаем вектор целых чисел, а не строк, и предположительно он не имеет размера!
Наконец, рассмотрим конструкторы, которые принимают несколько параметров - с «выводом»:
Какой параметр следует использовать для вывода? Нам понадобится какой-то способ сообщить компилятору, что это должен быть второй.
При всех этих проблемах для такого простого класса, как вектор, легко понять, почему логический вывод не используется.
источник
Сделав ctor шаблоном, переменная может иметь только одну форму, но разные ctors:
Видеть? Мы не можем иметь несколько членов Variable :: data.
источник
Дополнительную информацию см. В разделе «Вывод аргументов шаблона C ++» .
источник