簡體   English   中英

在模板功能和自動類型扣除之間進行選擇

[英]Choose between template function and auto type deduction

我有一個關於模板函數與函數自動類型推導的一般性問題。

多年來,我們已經能夠編寫模板功能:

template <class T> T add(T a,T b){
    return a+b;
}

有一個TS用於使用auto來進行函數的參數推導

auto add(auto a,auto b){
    return a+b;
}

我雖然使用auto,但是沒有辦法獲得實際類型,例如使用靜態成員,但這樣做很好:

#include <iostream>

struct foo
{
    static void bar(){std::cout<<"bar"<<std::endl;}
    static int i ;
};
int foo::i{0};
void t(auto f){
    decltype(f)::bar();
    std::cout<<    decltype(f)::i<<std::endl;
}
int main(int argc, char *argv[])
{
    t(foo());
    return 0;
}    

那么有沒有理由選擇一個而不是另一個?

這個特定代碼的明顯原因是它們根本沒有完全相同的語義。

特別是,如果傳遞不同類型的參數,則使用auto的版本將為每個參數獨立推導出一種類型,然后根據這些推斷出結果的類型。

相反,對於模板版本,您只指定了一種類型,因此參數必須都是相同的類型(結果將是相同的)。

所以,為了讓代碼更接近等價,你真的需要更像這樣編寫模板:

template <class T, class U> 
auto add(T a, U b) -> decltype(a+b) {
    return a+b;
}

這顯然也是可能的,但是為支持使用auto而增加了更多的優勢。

在您的代碼中, auto有兩種不同的用法,一種在參數中,另一種在返回類型中。 在參數的情況下,每次使用auto引入一個唯一的模板類型參數,因此Jerry提到它等同於:

// 1
template <typename A, typename B>
auto add(A a, B b) {
    return a + b;
}

在這種情況下,如果您對不同的類型參數有任何約束(必須相同,可能是一個,但會有其他),那么顯式模板語法提供了更好的替代方法。 如果你想在參數上使用SFINAE(通過一個額外的模板參數),這是特別的:

// 2
template <typename A, typename B, 
          typename _1 = typename A::iterator,  // A has nested iterator type
          typename _2 = typename B::iterator>  // So does B
auto f(A a, B b);

請注意,我故意避免在返回類型上使用SFINAE,因為它會以某種方式干擾您對auto的其他使用,但這可能是另一種選擇:

// 3
auto f(auto a, auto b) 
  ->     typename enable_if<has_nested_iterator<decltype(a)>::value
                         && has_nested_iterator<decltype(b)>::value, 
                           [return type] >::type;

但是你可以看到它變得更加復雜,因為你需要使用尾隨返回類型並通過decltype獲得類型的值。

在您的示例中第二次使用auto (與此示例完全不同)在返回類型中具有推導的返回類型 推導出的返回類型是一個在C ++ 11中已經可用於lambdas的功能,但已被推廣到所有功能模板。 該功能允許編譯器通過檢查正文中的不同return語句來查找函數返回的類型。 優點是,如果您的模板具有單個返回表達式,則可以避免必須兩次鍵入該表達式,一個用於實際代碼的返回類型另一個:

// 4
auto add(auto a, auto b) -> decltype(a + b) {  // 'a + b' here
   return a + b;                               // 'a + b' also here
}

缺點是編譯器需要檢查函數體以確定將返回的類型,這必然是post類型替換。 因此,具有推導類型的函數的return語句不能在SFINAE表達式中使用,這可能會使函數用戶的生命變得復雜:

// 5
auto doAdd(auto a, auto b) 
  -> typename enable_if<is_integral<decltype(add(a,b))>>::type
{
   return add(a,b);
}

如果您將其稱為doAdd(1, 1.)則SFINAE 不會從重載doAdd集中刪除上述doAdd重載,從而導致硬錯誤。

在思考之后,我看到使用模板功能的唯一原因是強制幾個參數具有相同的類型。 使用auto時,每種類型的演繹都是相互獨立的。

如果您希望第一個和第三個參數具有相同的類型,並且第二個參數具有相同類型,您甚至可以混合模板和自動。

template <class T> void barb(T a,auto b,T c){}
int main(int argc, char *argv[])
{
    barb(5," ",5); //ok 
    barb(5," ",5.6); //fails
    return 0;
} 

正如Oktalist在評論中所寫,您可以使用using語句來獲取參數的類型auto。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM