Подтвердить что ты не робот

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

Что не так в этом коде?

#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;
}
4b9b3361

Ответ 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 и декларации типов по-разному

Это хорошая вещь?