在哪些情况下需要专门指定模板的参数"类型"?
In which cases one needs to specify the template's argument `types` specifically?
// 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)
一种情况可能是需要指定返回类型。
是否存在其他需要手动指定参数类型的情况?
(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
如果函数模板参数出现在函数参数列表中,则不需要指定模板参数。例如,
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
这里的新东西是:类型参数的顺序也很重要。
无论如何,这只涵盖了基本概念。现在我建议你们读一些关于模板的好书,学习所有关于类型演绎的高级知识。
一般情况下,当编译器无法自行确定类型时,需要显式指定类型。正如您所提到的,当返回类型被模板化时,这种情况经常发生,因为返回类型不能从函数调用中推断出来。
模板类也有同样的问题——实例化std::vector
无法让编译器确定vector存储的类型,因此需要指定std::vector<int>
等等。
类型解析只在函数实参的情况下执行,因此将其视为特殊情况可能更容易;通常,编译器无法猜测要使用的类型。
简单的答案是,当编译器不能自行推断类型时,或者当您希望模板实例化为与编译器将推断的类型不同的特定类型时,您需要提供这些类型。
编译器不能推断类型有不同的情况。因为类型推导只应用于参数(就像重载解析的情况一样),如果返回类型不是作为可推导的参数出现,那么您必须指定它。但在其他情况下,类型演绎将不起作用:
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,了解从函数调用中推导参数的细节。
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型