В каких случаях нужно конкретно указать аргумент шаблона `types` - PullRequest
7 голосов
/ 29 августа 2011
// Function declaration.
template <typename T1, 
          typename T2, 
          typename RT> RT max (T1 a, T2 b);

// Function call.
max <int,double,double> (4,4.2)

// Function call.
max <int> (4,4.2)

Один случай может быть, когда вам нужно указать тип возвращаемого значения.

Существуют ли какие-либо другие ситуации, требующие указания типов аргументов вручную?

Ответы [ 4 ]

10 голосов
/ 29 августа 2011

(1) Когда не имеет аргумента для функции и все еще имеет тип template, тогда вам может потребоваться явно указать аргументы

template<typename T>
void foo ()
{}

Использование:

foo<int>();
foo<A>();

(2) Вы хотите различать значение и эталон .

template<typename T>
void foo(T obj)
{}

Использование:

int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference

(3) Требуется другой тип , который будет выведен вместо естественного типа.

template<typename T>
void foo(T& obj)
{}

Использование:

foo<double>(d);  // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>

(4) Для одного параметра шаблона предоставляются два различных типов аргументов

template<typename T>
void foo(T obj1, T obj2)
{}

Использование:

foo<double>(d,i);  // Deduction finds both double and int for T
7 голосов
/ 29 августа 2011

Если параметр шаблона функции появляется в списке параметров функции, вам не нужно указывать параметры шаблона.Например,

template<typename T>
void f(const T &t) {}

Здесь T - это параметр шаблона, и он появляется в списке параметров функции, т.е. const T &t.Таким образом, вам не нужно указывать параметр шаблона при вызове этой функции:

f(10); //ok

Поскольку тип из 10 равен int, поэтому компилятор может выведите из него параметр шаблона T, и T станет int.

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

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

template<typename T>
void g(const int &i) {}

Обратите внимание, g() отличается от f().Теперь T не отображается в списке параметров функции.Итак:

g(10); //error
g<double>(10); //ok

Обратите внимание, что если шаблон функции шаблонизируется и для типа возвращаемого значения, а тип возвращаемого значения отличается от типов, отображаемых в списке параметров функции, то вы должны указать тип возвращаемого значения:

template<typename T>
T h(const T &t) {}

Поскольку тип возвращаемого значения T совпадает с параметром функции, выведение типа возможно из аргумента функции:

h(10); //ok - too obvious now

Но если вы это:

template<typename R, typename T>
R m(const T &t) {}

Затем

m(10);  //error - only T can be deduced, not R
m<int>(10);  //ok

Обратите внимание, что хотя шаблон функции m шаблонизирован на два типа: R и T, мы предоставили только ОДИН тип при вызове,То есть мы написали m<int>(10) вместо m<int,int>(10).Нет ничего плохого в том, чтобы писать позже, но это нормально, если вы этого не сделаете.Но иногда вам приходится указывать оба, даже если можно вывести один тип T.Это когда порядок параметров типа отличается , как показано ниже:

template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}

Теперь вы должны предоставить оба типа:

n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok

Новыйдело здесь: порядок параметров типа также важен .

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

2 голосов
/ 29 августа 2011

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

Существуют разные обстоятельства, когда компилятор не может определить тип. Поскольку вычитание типа применяется только к аргументам (как в случае с разрешением перегрузки), если возвращаемый тип не отображается как выводимый аргумент, вам придется его указать. Но есть и другие обстоятельства, когда вывод типа не будет работать:

template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 );                 // Return type is deducible from arguments
min( 1.0, 2 );               // T is not deducible (no perfect match)
min<double>( 1.0, 2 );       // Now it is ok: forced to be double
min<double>( 1, 2 );         // Compiler will deduce int, but we want double

template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 );        // 0 is a valid T* for any T, select manually one

template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++;            // Type deduction will drop & by default and call
                             // min<int>(a,b), force the type to be a reference

template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() ); 
                                 // Argument type is not deducible, there are 
                                 // potentially infinite C that match the constraints
                                 // and the compiler would be forced to instantiate
                                 // all

Вероятно, есть и другие причины, по которым тип аргумента не может быть выведен, вы можете взглянуть на §14.8.2.1 в стандарте, чтобы узнать особенности вывода аргументов из вызова функции.

2 голосов
/ 29 августа 2011

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

У шаблонных классов есть та же проблема - создание экземпляра std::vector не дает никакого способакомпилятор, чтобы определить, какой тип хранит ваш вектор, поэтому вам нужно указать std::vector<int> и т. д.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...