Почему аргумент шаблона не может быть выведен, если он используется в качестве параметра шаблона для другого шаблона?
Что не так в этом коде?
#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); //Error: could not deduce template argument for T
return 0;
}
Ответы
Ответ 1
Это не выводимый контекст. Вот почему аргумент шаблона не может быть выведен компилятором.
Только представьте, если бы у вас был специализированный 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
is std::map<double, double>
, как компилятор узнает, есть ли T
SomeType
или OtherType
? Он даже не может знать, сколько таких вариантов он имеет, и он не может знать сам выбор...
Я просто прошу вас ради мысленного эксперимента (предполагая, что он может знать полный набор вариантов).
Ответ 2
Именно то, что сообщает сообщение об ошибке компилятора:
TMap<T>::Type
, T
не выводится в соответствии с
стандарт. Мотивация для этого, вероятно, заключается в том, что это не
технически возможно реализовать: компилятору придется
создайте все возможные TMap<T>
, чтобы увидеть, если один (и
только один) соответствует типу, который вы передали. И есть
бесконечное число TMap<T>
.
Ответ 3
Даже у вас есть:
TMap<SomeType>::Type = std::map<double, double>.
Но прежде чем вы вызовете тест (tmap)
TMap<double>::Type tmap;
tmap[1.1] = 5.2;
double d = test(tmap);
Вы уже объявили его как
TMap<double>::Type tmap;
почему эта информация не может быть использована. #typedef - это не просто замена строки.
Ответ 4
Я не думаю, что "мы не можем этого сделать" аргумент правильный.
Если мы немного изменим этот пример, компилятор с радостью выведет аргументы для нас.
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); // compiles just fine.
Я не вижу огромной разницы между исходным примером и моим. Настоящая проблема здесь кажется, что С++ обрабатывает typedefs и декларации типов по-разному
Это хорошая вещь?