簡體   English   中英

模板專業化和完美轉發

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

如果Xint ,則X&&int&& 如果Xint& ,則X&&int&

重讀。 再次。 再一次。 同時應用時, &勝過&&

接下來,演繹。 我會在這里說謊言,但它們只是在簡化謊言。

如果將Foo&傳遞給推導X&&的模板,則XFoo&X&&Foo&

如果將Foo&&傳遞給推導X&&的模板,則XFooX&&Foo&&

接下來, std::forward是有條件的動作。 對於參考變量X&& xstd::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::stringsanitize_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.

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