1.3 Multiple Template Parameters
1.3 多模板参数
As we have seen so far, function templates have two distinct sets of parameters:
到目前为止,我们己经看到函数模板具有两组不同的参数:
1. Template parameters, which are declared in angle brackets before the function template name:
1. 模板参数,在函数模板名称之前的尖括号中声明:
template<typename T> // T is template parameter
2. Call parameters, which are declared in parentheses after the function template name:
2.调用参数(此处指形参),在函数模板名称之后的圆括号中声明:
T max (T a, T b) // a and b are call parameters
You may have as many template parameters as you like. For example, you could define the max() template for call parameters of two potentially different types:
你可以根据需要设计拥用任意数量的模板参数。例如,你可以为两种可能不同类型的形参定义max()模板:
template<typename T1, typename T2> T1 max (T1 a, T2 b) { return b < a ? a : b; } … auto m = ::max(4, 7.2); // OK, 但是返回类型由第1个参数类型决定
It may appear desirable to be able to pass parameters of different types to the max() template, but, as this example shows, it raises a problem. If you use one of the parameter types as return type, the argument for the other parameter might get converted to this type, regardless of the caller’s intention. Thus, the return type depends on the call argument order. The maximum of 66.66 and 42 will be the double 66.66, while the maximum of 42 and 66.66 will be the int 66. C++ provides different ways to deal with this problem:
这似乎是一种能够给max()模板传递两个不同类型参数的好方法。但是,正如本例所示,这会带来一个问题。如果你将其中一个参数类型作为返回值类型,那么,无论调用者的意图如何,另一个参数的实参都可能转换为该类型。于是,函数的返回类型取决于调用参数的顺序。66.66和42的最大值为浮点型66.66,而42和66.66的最大值为整型66。C++提供不同的方法来处理这个问题:
• Introduce a third template parameter for the return type.
为返回类型引入第3个模板参数
• Let the compiler find out the return type.
让编译器找出返回类型
• Declare the return type to be the “common type” of the two parameter types.
将返回类型声明为两个参数类型的“公共类型”
All these options are discussed next.
以上几点将在后面讨论。
1.3.1 Template Parameters for Return Types
1.3.1 返回类型的模板参数
Our earlier discussion showed that template argument deduction allows us to call function templates with syntax identical to that of calling an ordinary function: We do not have to explicitly specify the types corresponding to the template parameters.
我们之前的讨论表明,模板参数推导允许我们可以使用与调用普通函数相同的语法来调用函数模板:我们不必显式指定与模板参数相对应的类型。
We also mentioned, however, that we can specify the types to use for the template parameters explicitly:
但是,我们还提到了可以明确指定模板参数的类型:
template<typename T> T max (T a, T b); …: :max<double>(4, 7.2); // instantiate T as double(将T实例化为double类型)
In cases when there is no connection between template and call parameters and when template parameters cannot be determined, you must specify the template argument explicitly with the call. For example, you can introduce a third template argument type to define the return type of a function template:
当模板参数和调用参数没有关联,或者不能由调用参数来决定模板参数的时候,在调用时就必须显式指定模板实参。例如,你可以引入第3个模板实参类型来定义函数模板的返回类型:
template<typename T1, typename T2, typename RT>
RT max (T1 a, T2 b);
However, template argument deduction does not take return types into account, and RT does not appear in the types of the function call parameters. Therefore, RT cannot be deduced.
然而,模板实参推导并不适合返回类型。因为RT不会出现在形参的类型里面。因此,RT是不能被推导的。
As a consequence, you have to specify the template argument list explicitly. For example:
于是,你必须显式地指定模板实参列表,例如:
template<typename T1, typename T2, typename RT> RT max (T1 a, T2 b); … : :max<int,double,double>(4, 7.2); // OK, but tedious(可以,但很枯燥乏味)
So far, we have looked at cases in which either all or none of the function template arguments were mentioned explicitly. Another approach is to specify only the first arguments explicitly and to allow the deduction process to derive the rest. In general, you must specify all the argument types up to the last argument type that cannot be determined implicitly. Thus, if you change the order of the template parameters in our example, the caller needs to specify only the return type:
到目前为止,我们只考察了显式指定所有函数模板实参的例子,和不显式指定函数任何实参类型的例子。另一种情况是只显式指定第一个实参,而让推导过程推断出其余的类型。通常而言,你必须指定所有实参类型,直到最后一个不能被隐式推导出来的类型为止。因此,在上面的例子中,如果你改变模板参数的声明顺序,那么调用者只需要指定返回类型即可:
template<typename RT, typename T1, typename T2> RT max (T1 a, T2 b); … : :max<double>(4, 7.2) //OK: return type is double, T1 and T2 are deduced
In this example, the call to max<double> explicitly sets RT to double, but the parameters T1 and T2 are deduced to be int and double from the arguments.
在这个例子里,调用max<double>时显式地将RT指定为double,但其他两个参数T1和T2可以从实参中分别推导为int和double
Note that these modified versions of max() don’t lead to significant advantages. For the one-parameter version you can already specify the parameter (and return) type if two arguments of a different type are passed. Thus, it’s a good idea to keep it simple and use the one-parameter version of max() (as we do in the following sections when discussing other template issues).
可以看出,所有这些修改后的max()版本都不能得到很大的改进。由于单模板参数的版本里,如果传递进来的是两个不同类型的实参,你也可以事先指定参数类型(和返回值类型)。因此,尽量保持简洁并且使用单参数版本的max()是一个不错的主意(在接下来的几节里,当讨论其他模板话题的时候,我们将使用这种方法)
See Chapter 15 for details of the deduction process.
关于推导过程的更多内容请参见第15章。
1.3.2 Deducing the Return Type
1.3.2 推导返回类型
If a return type depends on template parameters, the simplest and best approach to deduce the return type is to let the compiler find out. Since C++14, this is possible by simply not declaring any return type (you still have to declare the return type to be auto):
当一个返回值类型依赖于模板参数时,推导返回类型最简单也是最好的方法就是让编译器去找出。从C++14开始,可以通过不声明任何类型(你可能仍然需要将返回类型声明为auto)来实现:
template<typename T1, typename T2> auto max (T1 a, T2 b) { return b < a ? a : b; }
In fact, the use of auto for the return type without a corresponding trailing return type (which would be introduced with a -> at the end) indicates that the actual return type must be deduced from the return statements in the function body. Of course, deducing the return type from the function body has to be possible. Therefore, the code must be available and multiple return statements have to match.
实际上,返回类型使用auto而不使用相应返回值尾随类型(以->结尾),表明实际的返回类型必须在函数体的返回语句中被推导出来。当然,这要求必须可以从函数体推导出返回类型。因此,代码必须是有效的,同时多个返回语句时必须匹配。
Before C++14, it is only possible to let the compiler determine the return type by more or less making the implementation of the function part of its declaration. In C++11 we can benefit from the fact that the trailing return type syntax allows us to use the call parameters. That is, we can declare that the return type is derived from what operator?: yields:
在C++14之前,只能让编译器通过或多或少地将函数的实现作为其声明的一部分来确定返回类型。在C++11中,尾随返回类型语法允许我们使用形参,我们可以该语法中受益。也就是说,我们可以声明的返回类型是从运算符“?:”的结果中生成的。
template<typename T1, typename T2> auto max (T1 a, T2 b) -> decltype(b<a?a:b) { return b < a ? a : b; }
Here, the resulting type is determined by the rules for operator ?:, which are fairly elaborate but generally produce an intuitively expected result (e.g., if a and b have different arithmetic types, a common arithmetic type is found for the result).
此处,结果类型由运算符“?:”的规则确定,该规则比较复杂,但通常会产生直观的预期结果(例如,如果a和b具有不同的算术类型,则会为该结果找到一个公共的算术类型)
Note that
注意
template<typename T1, typename T2>
auto max (T1 a, T2 b) -> decltype(b<a?a:b);
is a declaration, so that the compiler uses the rules of operator?: called for parameters a and b to find out the return type of max() at compile time. The implementation does not necessarily have to match. In fact, using true as the condition for operator?: in the declaration is enough:
是一个声明,因此编译器使用运算符“?:”的运算规则(参数为a和b),在编译期找出max()的返回类型。在实现中并不要求一定要匹配,实际上在声明中使用true作为operator>:的条件己经足够了:(译者注:三目运算符的运算规则,返回类型为a和b的最高类型,期间可能会进行隐式类型转换。但返回类型与条件真或假无关)
template<typename T1, typename T2> auto max (T1 a, T2 b) -> decltype(true?a:b);
However, in any case this definition has a significant drawback: It might happen that the return type is a reference type, because under some conditions T might be a reference. For this reason you should return the type decayed from T, which looks as follows:
但是,无论如何,该定义都有一个明显的缺点:因为在某些情况下,T可能是个引用类型,因此返回类型可能是引用由于这个原因,你应该返回T的退化类型,如下所示:
#include <type_traits> template<typename T1, typename T2> auto max (T1 a, T2 b) -> typename std::decay<decltype(true?a:b)>::type { return b < a ? a : b; }
Here, the type trait std::decay<> is used, which returns the resulting type in a member type. It is defined by the standard library in <type_trait> (see Section D.5 on page 732). Because the member type is a type, you have to qualify the expression with typename to access it (see Section 5.1 on page 67).
在这里,使用了std::decay<>类型萃取,其的type成员返回的是结果类型。std::decay<>定义在标准库<type_trait>中(见第732页的D.5节)。由于type成员是一个类型,所以必须使用typename来限制表达式,以便访问它(见67页5.1节)。
Note that an initialization of type auto always decays. This also applies to return values when the return type is just auto. auto as a return type behaves just as in the following code, where a is declared by the decayed type of i, int:
注意,auto类型的初始化始终是退化的类型。当函数返回类型只有auto时,该规则也适用于返回类型。auto作为返回类型的行为与以下代码相同,其中a的类型是i的退化类型(即int):
int i = 42; int const& ir = i; // ir refers to i auto a = ir; // a is declared as new object of type int
1.3.3 Return Type as Common Type
1.3.3 作为通用类型返回
Since C++11, the C++ standard library provides a means to specify choosing “the more general type.” std::common_type<>::type yields the “common type” of two (or more) different types passed as template arguments. For example:
从C++11开始,C++标准库提供了一种用来指定并选择“更通用的类型”的方法。std::common_type<>::type为两个(或多个)不同的模板参数产生一个“共同类型”。例如:
#include <type_traits> template<typename T1, typename T2> std::common_type_t<T1,T2> max (T1 a, T2 b) { return b < a ? a : b; }
Again, std::common_type is a type trait, defined in <type_traits>, which yields a structure having a type member for the resulting type. Thus, its core usage is as follows:
同样,std::common_type是定义在<type_traits>中的一个类型萃取工具,它会产生一个具有type成员的结构体,该成员用于表示结果的类型。因此,其核心用法如下:
typename std::common_type<T1,T2>::type //since C++11
However, since C++14 you can simplify the usage of traits like this by appending _t to the trait name and skipping typename and ::type (see Section 2.8 on page 40 for details), so that the return type definition simply becomes:
但是,从C++14开始,你可以通过在萃取机的名称后面附加_t并省略typename和::type来简化其使用方法(详见第40页的2.8节)
std::common_type_t<T1,T2> // equivalent since C++14(等价定义,从C++14起)
The way std::common_type<> is implemented uses some tricky template programming, which is discussed in Section 26.5.2 on page 622. Internally, it chooses the resulting type according to the language rules of operator ?: or specializations for specific types. Thus, both ::max(4, 7.2) and ::max(7.2, 4) yield the same value 7.2 of type double. Note that std::common_type<>also decays. See Section D.5 on page 732 for details.
std::common_type<>的实现中使用了一些模板编程技巧,这会第622页的26.5.2节中进行讨论。在内部,它根据运算符“?:”的一些语言规则或具体类的特化来选择结果的类型。因此,不管是::max(4, 7.2)还是::max(7.2, 4)都会产生相同的浮点型数值7.2。注意,std::common_type<>也是一个退化类型。有关详细信息,请参考第732页的D.5节。