Почему T не может быть выведен из Template<T>::Type?
Вопрос: Что не так с этим кодом?
У меня есть следующий код на C++, но компилятор выдает ошибку "could not deduce template argument for T". Я не понимаю, что именно вызывает эту ошибку. Вот сам код:
#include <map>
template<typename T>
struct TMap
{
typedef std::map<T, T> Type;
};
template<typename T>
T test(typename TMap<T>::Type &tmap_) { return 0.0; }
int _tmain(int argc, _TCHAR* argv[])
{
TMap<double>::Type tmap;
tmap[1.1] = 5.2;
double d = test(tmap); //Ошибка: could not deduce template argument for T
return 0;
}
В строке double d = test(tmap);
я получаю ошибку, связанную с шаблоном. Не могу понять, как правильно передать аргумент шаблона. Что мне нужно сделать, чтобы исправить эту ошибку и компилировать код без проблем?
4 ответ(ов)
Это недедуктивный контекст. Вот почему аргумент шаблона не может быть выведен компилятором.
Представьте себе, что вы могли бы специализировать TMap
следующим образом:
template <>
struct TMap<SomeType>
{
typedef std::map<double, double> Type;
};
Как компилятор мог бы вывести тип SomeType
, если TMap<SomeType>::Type
— это std::map<double, double>
? Он не может. Не гарантируется, что тип, который вы используете в std::map
, также является типом в TMap
. Компилятор не может делать это опасное предположение. Между аргументами типов может не быть вообще никакой связи.
Кроме того, возможно, у вас есть еще одна специализация TMap
, определенная так:
template <>
struct TMap<OtherType>
{
typedef std::map<double, double> Type;
};
Это усугубляет ситуацию. Теперь у вас есть следующее:
TMap<SomeType>::Type
=std::map<double, double>
.TMap<OtherType>::Type
=std::map<double, double>
.
Теперь спросите себя: если TMap<T>::Type
— это std::map<double, double>
, как компилятор может узнать, является ли T
SomeType
или OtherType
? Он даже не может знать, сколько таких вариантов у него есть, не говоря уже о самих выборах...
Я просто прошу вас подумать об этом как о мысленном эксперименте (предполагая, что он может знать полный набор выборов).
Согласно точному тексту сообщения об ошибке компилятора: в TMap<T>::Type
тип T
не может быть выведен согласно стандарту. Причина этого, вероятно, в том, что это технически невозможно реализовать: компилятор должен будет создать экземпляры всех возможных TMap<T>
, чтобы определить, соответствует ли один (и только один) из них переданному вами типу. А количество возможных TMap<T>
бесконечно.
Судя по вашему вопросу, вы пытаетесь понять, почему использование типа, заданного с помощью typedef
или using
, не работает так, как ожидается, когда вы передаете его в функцию.
В вашем примере:
TMap<SomeType>::Type = std::map<double, double>;
При этом, когда вы создаете экземпляр:
TMap<double>::Type tmap;
Вы создаете объект переменной tmap
типа std::map<double, double>
, согласно тому, как определен ваш TMap
.
Обратите внимание на то, что typedef
(или using
) — это просто механизм, позволяющий задавать синонимы для типов, и он не создает новый тип. Вместо этого он предоставляет возможность использовать более удобное имя для уже существующего типа.
Теперь, когда вы вызываете test(tmap)
, важно понимать, как устроены параметры функции. Например, если функция test
принимает параметр типа, который не совпадает с тем, что вы передаете, компилятор выдаст ошибку.
Возникает ситуация, что если test
принимает в качестве параметра, например, std::map<int, int>
, а вы передаете std::map<double, double>
, это приведет к несоответствию типов.
Если вы уже объявили TMap<double>::Type
как std::map<double, double>
, убедитесь, что функция, которую вы вызываете, принимает именно этот тип. Если у вас есть некое преобразование или mismatch с параметрами, это может стать причиной проблемы.
Если вы получите ошибку компиляции, это означает, что несовпадение типов не было устранено. Поэтому важно проверять, соответствует ли тип в вызове функции ожидаемому типу параметра. Как правило, лучше всего всегда следить за тем, какие типы используются в функциях и где они определяются.
Если у вас возникли дополнительные вопросы, не стесняйтесь задать их!
Ваша точка зрения о том, что аргумент "мы не можем это сделать" неверен, имеет свои основания. Если немного изменить исходный пример, то компилятор с удовольствием будет выводить аргументы за нас.
template<typename T>
struct TMap //...
template <class T>
struct tmap_t : TMap<T>::Type {};
template<typename T>
T test(tmap_t<T> tmap) // ...
tmap_t<double> tmap; // ...
double d = test(tmap); // компилируется без проблем.
Между вашим примером и оригиналом я не вижу большой разницы. По всей видимости, настоящая проблема заключается в том, что C++ различает typedef
и объявления типов.
Что касается вопроса, является ли это хорошей практикой, то тут мнения могут различаться. Одной из причин подобного поведения является сложность системы типов в C++, где typedef
может быть совмещен с другими конструкциями языка, что приводит к путанице. Например, использование typedef
позволяет создавать синонимы типов, а using
может допускать более сложные шаблонные конструкции. Это может быть как плюсом, так и минусом, в зависимости от ситуации и способностей программиста.
Таким образом, ваше замечание о различии в трактовке typedef
и объявлений типов совершенно верно и действительно является центральным моментом, который стоит учитывать при проектировании шаблонов и интерфейсов в C++.
В чем разница между параметрами шаблона "typename" и "class"?
Что означают int argc, char *argv[]?
Хранение определений шаблонных функций C++ в .CPP файле
QT: Шаблонизированный класс с Q_OBJECT
Специализация шаблонного класса, где аргументом шаблона является другой шаблон