[英]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.