0

Почему T не может быть выведен из Template<T>::Type?

38

Вопрос: Что не так с этим кодом?

У меня есть следующий код на 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 ответ(ов)

0

Это недедуктивный контекст. Вот почему аргумент шаблона не может быть выведен компилятором.

Представьте себе, что вы могли бы специализировать 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? Он даже не может знать, сколько таких вариантов у него есть, не говоря уже о самих выборах...

Я просто прошу вас подумать об этом как о мысленном эксперименте (предполагая, что он может знать полный набор выборов).

0

Согласно точному тексту сообщения об ошибке компилятора: в TMap<T>::Type тип T не может быть выведен согласно стандарту. Причина этого, вероятно, в том, что это технически невозможно реализовать: компилятор должен будет создать экземпляры всех возможных TMap<T>, чтобы определить, соответствует ли один (и только один) из них переданному вами типу. А количество возможных TMap<T> бесконечно.

0

Судя по вашему вопросу, вы пытаетесь понять, почему использование типа, заданного с помощью 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 с параметрами, это может стать причиной проблемы.

Если вы получите ошибку компиляции, это означает, что несовпадение типов не было устранено. Поэтому важно проверять, соответствует ли тип в вызове функции ожидаемому типу параметра. Как правило, лучше всего всегда следить за тем, какие типы используются в функциях и где они определяются.

Если у вас возникли дополнительные вопросы, не стесняйтесь задать их!

0

Ваша точка зрения о том, что аргумент "мы не можем это сделать" неверен, имеет свои основания. Если немного изменить исходный пример, то компилятор с удовольствием будет выводить аргументы за нас.

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++.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь