[英]Template specialization and perfect forwarding
因此,我試圖構建一個“消毒程序”功能來過濾格式化參數,然后再將其轉發給printf
。
template<typename A>
_CR_INLINE decltype(auto) sanitize_forward(A&& arg) {
return std::forward<A>(arg);
}
template<>
_CR_INLINE decltype(auto) sanitize_forward<std::string>(std::string&& arg) {
return std::forward<const char*>(arg.c_str());
}
因此,每個std::string
都應該“分解”為const char*
,以便正確格式化。
template<typename...Args>
_CR_INLINE void printf_safe(const std::string& format, Args&&...args) {
std::printf(format.c_str(), sanitize_forward<Args>(args)...);
}
我希望將參數完美地轉發到printf
,這就是為什么我返回std::forward
。 但是我真的無法確定應該如何實現。
1) decltype(auto)
是否正確? 它應該保留std::forward
返回的r值參考,對嗎?
2)我應該如何專門化我的模板: std::string&&
或std::string
?
3)推導的轉發引用應該與實際類型相同,對嗎?
您幾乎完全誤解了轉發參考; 可以預料的是,它們令人困惑。
要理解它們,您必須了解引用折疊規則, std::forward
本身,推論T&&
時的模板參數推導規則,以及它們如何結合在一起(至少在某種程度上)。
首先,參考崩潰。 看這張圖:
If Then Then
X is X& is X&& is
T T& T&&
T& T& T&
T const& T const& T const&
T&& T& T&&
如果X
為int
,則X&&
為int&&
。 如果X
為int&
,則X&&
為int&
。
重讀。 再次。 再一次。 同時應用時, &
勝過&&
。
接下來,演繹。 我會在這里說謊言,但它們只是在簡化謊言。
如果將Foo&
傳遞給推導X&&
的模板,則X
為Foo&
而X&&
為Foo&
。
如果將Foo&&
傳遞給推導X&&
的模板,則X
為Foo
且X&&
為Foo&&
。
接下來, std::forward
是有條件的動作。 對於參考變量X&& x
, std::forward<X>(x)
是decltype(x)(x)
-將x
強制轉換為聲明為的類型。 如果x
是右值引用,則將x
轉換為右值引用。 這是必需的,因為即使x
是右值引用,表達式x
的類型也是X&
而不是X&&
。 一個rvalue引用是對一個rvalue的引用,但本身不是一個rvalue。
現在修復您的代碼。
template<class T>
struct tag_t{constexpr tag_t(){}};
template<class T>
constexpr tag_t<std::decay_t<T>> tag{};
template<class T>
auto sanitizer(tag_t<T>){
return [](auto&& t)->decltype(auto){
return decltype(t)(t);
};
}
template<class A>
decltype(auto) sanitize(A&& arg) {
return sanitizer(tag<A>)(std::forward<A>(arg));
}
auto sanitizer(tag_t<std::string>) {
return [](std::string const& s){return s.c_str();};
}
template<class...Ts>
void printf_safe(const std::string& format, Ts&&...ts) {
std::printf(format.c_str(), sanitize(std::forward<Ts>(ts))...);
}
我遵循SRP(單一責任原則)-我從消毒中分離出來。
然后我分裂,其消毒作用( sanitizer
從實際去做)( sanitize
)。
這讓我只編寫一次std::string
結束符代碼,而不會因“偶然”而獲得完美的轉發。
順便說一句,如果要將數組args視為非指針,我將用remove ref替換衰減,並刪除cv。
您可以擴展sanitize
寫的功能sanitizer
中的任意命名空間重載tag_t
或類型的命名空間,我們消毒(不位在std
自然)。
因此,我試圖構建一個“消毒程序”功能來過濾格式化參數,然后再將其轉發給
printf
。
您的前提似乎不對。 printf
是一個使用va_arg
的C函數-它不執行或不需要任何完美的轉發。
http://en.cppreference.com/w/cpp/io/c/fprintf
它也永遠不會“消耗”其參數,而只是從它們中讀取。 區分臨時對象和非臨時對象沒有任何意義-只需在printf
包裝器中使用const Args&...
1)
decltype(auto)
是否正確? 它應該保留std::forward
返回的r值參考,對嗎?
std::forward
將返回左值引用或右值引用 。 decltype(auto)
確實會保留該設置。
您對std::string
的sanitize_forward
專業化看起來並不有用std::forward<const char*>
將始終返回const char* &&
。 我認為這與以下內容沒有什么不同:
template<>
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) {
return arg.c_str();
}
另外,返回.c_str()
作為對std::string
的右值引用聽起來非常危險.c_str()
正確:您正在使用一個指針,該指針指向即將到期的字符串的內部緩沖區。 您可能想在此處使用const std::string&
。
2)我應該如何專門化我的模板:
std::string&&
或std::string
?
怎么稱呼它? 您是否明確提供模板參數? 模板參數將始終是非引用 ,還是既是左值引用又是非引用 ?
由於您具有sanitize_forward<Args>
,因此您可能會嘗試同時調用這兩個方法。
sanitize_forward<std::string>
sanitize_forward<std::string&>
...可能帶有簡歷限定詞 。 您可能想提供一個額外的顯式std::decay_t<Args>
參數,用於處理“專業化”業務。
3)推導的轉發引用應該與實際類型相同,對嗎?
不知道這是什么意思。 您能詳細說明一下嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.