天天看點

第1章 函數模闆:1.5 重載函數模闆

1.5 Overloading Function Templates

1.5 重載函數模闆

Like ordinary functions, function templates can be overloaded. That is, you can have different function definitions with the same function name so that when that name is used in a function call, a C++ compiler must decide which one of the various candidates to call. The rules for this decision may become rather complicated, even without templates. In this section we discuss overloading when templates are involved. If you are not familiar with the basic rules of overloading without templates, please look at Appendix C, where we provide a reasonably detailed survey of the overload resolution rules.

和普通函數一樣,函數模闆也可以被重載。就是說,相同的函數名可以有不同的定義:于是,當使用函數名稱進行函數調用時,C++編譯器必須決定究竟要調用哪個候選函數。即使在不考慮模闆的情況下,做出該決定的規則也己經是相當的複雜。本節中,我們将讨論有關模闆的重載問題。如果你對不含模闆的重載的基本規則還不是很熟悉,那麼請先閱讀附錄C,在那裡我們對重載解析規則進行了很詳細的叙述。

The following short program illustrates overloading a function template:

下面的簡短程式叙述了如何重載一個函數模闆:

// maximum of two int values:(求兩個整數的最大值)
int max (int a, int b)
{
    return b < a ? a : b;
}

// maximum of two values of any type:(求任意類型兩個數的最大值)
template<typename T>
T max (T a, T b)
{
    return b < a ? a : b;
}

int main()
{
    ::max(7, 42); // calls the nontemplate for two ints(調用兩個int參的非模闆函數)
    ::max(7.0, 42.0); // calls max<double> (by argument deduction)
    ::max(’a’, ’b’); //calls max<char> (by argument deduction)
    ::max<>(7, 42); // calls max<int> (by argument deduction)
    ::max<double>(7, 42); // calls max<double> (no argumentdeduction)
    ::max(’a’, 42.7); //calls the nontemplate for two ints
}      

As this example shows, a nontemplate function can coexist with a function template that has the same name and can be instantiated with the same type. All other factors being equal, the overload resolution process prefers the nontemplate over one generated from the template. The first call falls under this rule:

如示例所示,一個非模闆函數可以和一個同名的函數模闆同時存在,而且該該函數模闆還可以被執行個體化為這個非模闆函數。同等條件下,重載解析過程通常會調用非模闆函數,而不是調用模闆産生出的執行個體。第1個調用就符合這個規則:

::max(7, 42); // both int values match the nontemplate function perfectly      

If the template can generate a function with a better match, however, then the template is selected. This is demonstrated by the second and third calls of max():

然而,如果模闆可以産生一個更好的比對,那麼将選擇模闆。這可以通過第2和第3個的max()調用來說明:

::max(7.0, 42.0); // calls the max<double> (by argument deduction)
::max(’a’, ’b’);  //calls the max<char> (by argument deduction)      

Here, the template is a better match because no conversion from double or char to int is required (see Section C.2 on page 682 for the rules of overload resolution).

這裡,模闆具有更好的比對,因為不需要從double或char到int型的轉換(可見第682頁的C.2節《重載解析規則》)

It is also possible to specify explicitly an empty template argument list. This syntax indicates that only templates may resolve a call, but all the template parameters should be deduced from the call arguments:

也可以顯式指定一個空模闆參數清單。此文法表明隻有模闆可以解析該調用,但所有的模闆參數都應該從調用實參中推導出來。

::max<>(7, 42); // calls max<int> (by argument deduction)      

Because automatic type conversion is not considered for deduced template parameters but is considered for ordinary function parameters, the last call uses the nontemplate function (while ’a’ and 42.7 both are converted to int):

因為模闆是不允許自動類型轉化的;但普通函數可以進行自動類型轉換,是以最後一個調用将使用非模闆函數('a'和42.7都被轉化為int):

::max(’a’, 42.7); //only the nontemplate function allows nontrivial conversions      

An interesting example would be to overload the maximum template to be able to explicitly specify the return type only:

一個有趣的例子是重載max的模闆,以便隻可以顯式傳回類型:

template<typename T1, typename T2>
auto max (T1 a, T2 b)
{
    return b < a ? a : b;
}

template<typename RT, typename T1, typename T2>
RT max (T1 a, T2 b)
{
    return b < a ? a : b;
}

       

Now, we can call max(), for example, as follows:

現在, 我們可以調用max(),示例如下:

auto a = ::max(4, 7.2); // uses first template
auto b = ::max<long double>(7.2, 4); // uses second template      

However, when calling:

但是,當我們調用:

auto c = ::max<int>(4, 7.2); // ERROR: both function templates match      

both templates match, which causes the overload resolution process normally to prefer none and result in an ambiguity error. Thus, when overloading function templates, you should ensure that only one of them matches for any call.

兩個模闆都比對,通常這将導緻重載決議過程不選擇任何一個,并且導緻二義性錯誤。是以,當重載函數模闆時,你必需確定在任何調用時,他們中隻有一個與之比對。

A useful example would be to overload the maximum template for pointers and ordinary C-strings:

下面這個更有用的例子将會為指針和普通的C字元串重載這個求最大值的模闆。

#include <cstring>
#include <string>

// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{
    return b < a ? a : b;
}

// maximum of two pointers:
template<typename T>
T* max (T* a, T* b)
{
    return *b < *a ? a : b;
}

// maximum of two C-strings:
char const* max (char const* a, char const* b)
{
    return std::strcmp(b,a) < 0 ? a : b;
}

int main ()
{
    int a = 7;
    int b = 42;
    auto m1 = ::max(a,b); // max() for two values of type int

    std::string s1 = "hey"; "
    std::string s2 = "you"; "
    auto m2 = ::max(s1,s2); // max() for two values of type std::string

    int* p1 = &b;
    int* p2 = &a;
    auto m3 = ::max(p1,p2); // max() for two pointers

    char const* x = hello"; "
    char const* y = "world"; "
    auto m4 = ::max(x,y); // max() for two C-strings
}      

Note that in all overloads of max() we pass the arguments by value. In general, it is a good idea not to change more than necessary when overloading function templates. You should limit your changes to the number of parameters or to specifying template parameters explicitly. Otherwise, unexpected effects may happen. For example, if you implement your max() template to pass the arguments by reference and overload it for two C-strings passed by value, you can’t use the three-argument version to compute the maximum of three C-strings:

注意,在max()的所有重載中,我們都是按值傳遞參數的。一般而言,在重載函數的時候,最好隻是改變那些需要改變的内容:就是說,你應該把你的改變限制在下面兩種情況:改變參數的數目或者顯式指定模闆參數。否則,就可能會出現非預期的結果。例如,對于原來使用傳引用的max()模闆,但現在重載基于C-strings的max模闆是按值傳遞參數的,那麼你就不能使用帶3個參數版本的max模闆來求C-strings的最大值。

#include <cstring>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max(T const& a, T const& b)
{
    return b < a ? a : b;
}

// maximum of two C-strings (call-by-value)求兩個C字元串的最大值(按值傳參)
char const* max(char const* a, char const* b)
{
    return std::strcmp(b, a) < 0 ? a : b;
}

// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max(T const& a, T const& b, T const& c)
{
    return max(max(a, b), c); // error if max(a,b) uses call-by-value
}

int main()
{
    auto m1 = ::max(7, 42, 68); // OK

    char const* s1 = "frederic";
    char const* s2 = "anica";
    char const* s3 = "lucas";
    auto m2 = ::max(s1, s2, s3); //run-time ERROR
}      

The problem is that if you call max() for three C-strings, the statement

問題在于:如果你對3個C-strings調用max(),那麼語句:

return max (max(a,b), c);      

becomes a run-time error because for C-strings, max(a,b) creates a new, temporary local value that is returned by reference, but that temporary value expires as soon as the return statement is complete, leaving main() with a dangling reference. Unfortunately, the error is quite subtle and may not manifest itself in all cases.

将産生一個運作期錯誤,這是因為對于C-strings而言,這裡的max(a,b)産生了一個新的臨時局部值,該值可能被外面的max(即三個參數的max)以傳引用的方式傳回,而該臨時值在return語句結束後将立即失效,main()函數将保留懸垂引用。不幸的是,該錯誤非常隐蔽,可能無法在所有的情況下均顯示出來。

Note, in contrast, that the first call to max() in main() doesn’t suffer from the same issue. There temporaries are created for the arguments (7, 42, and 68), but those temporaries are created in main() where they persist until the statement is done.

請注意,與此相反,在main()函數對max()的首次調用不會遇到相同的問題。會為參數(7、42和68)建立臨時對象,但是那些臨時對象是在main()中建立的,并且會持續存在,直至語句結束。

This is only one example of code that might behave differently than expected as a result of detailed overload resolution rules. In addition, ensure that all overloaded versions of a function are declared before the function is called. This is because the fact that not all overloaded functions are visible when a corresponding function call is made may matter. For example, defining a three-argument version of max() without having seen the declaration of a special two-argument version of max() for ints causes the two-argument template to be used by the three-argument version:

#include <iostream>

// maximum of two values of any type:
template<typename T>
T max(T a, T b)
{
    std::cout << "max<T>() \n";
    return b < a ? a : b;
}

// maximum of three values of any type:(求任意類型的3個值的最大者)
template<typename T>
T max(T a, T b, T c)
{
    return max(max(a, b), c); // uses the template version even for ints
                              //because the following declaration comes
                              // too late:
}

// maximum of two int values:(求兩個int值的最大者)
int max(int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

int main()
{
    ::max(47, 11, 33); // OOPS: uses max<T>() instead of max(int,int)
}