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

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

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

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>,

Даже у вас есть:

TMap<SomeType>::Type = std::map<double, double>. 

Но прежде чем вызвать test(tmap)

TMap<double>::Type tmap;
tmap[1.1] = 5.2;
double d = test(tmap); 

Вы уже объявили это

TMap<double>::Type tmap;

почему эта информация не может быть использована. #typedef - это не просто замена строк.

Я не думаю, что аргумент "мы не можем сделать это" является правильным. Если мы немного изменим этот пример, компилятор с удовольствием выведет для нас аргументы.

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.

Я не вижу большой разницы между оригинальным примером и моим. Настоящая проблема здесь заключается в том, что C++ по-разному относится к определениям типов и объявлениям типов.

Это хорошо?

Другие вопросы по тегам