2017-08-04 62 views
4

我有兩個功能C++ SFINAE解析順序

template <typename... Args> 
void foo(Args&&... args) { /* ... */ } 

template <typename... Args> 
void foo(const std::string& name, Args&&... args) { /* ... */ } 

目前像foo("bar", /* arguments */)所有的呼叫嘗試去的第一個函數,而不是第二。我想重新排序這些函數,以便SFINAE在第一個之前找到第二個函數。我無法使用std::enable_if來檢查字符數組/字符串,因爲Args...數據包可能包含std::string&const char (&) []。我該怎麼做呢?

回答

6

這裏的問題是,"bar"不是std::string。沒有任何數量的重新排序將被調用void foo(const std::string& name, Args&&... args),因爲這需要轉換,而void foo(Args&&... args)將產生完全匹配。

一種解決方法是,您可以使用literal string operator並使"bar"爲使用"bar"s的字符串。這確實需要改變

template <typename... Args> 
void foo(const std::string& name, Args&&... args) { /* ... */ } 

template <typename... Args> 
void foo(std::string&& name, Args&&... args) { /* ... */ } 

template <typename... Args> 
void foo(std::string& name, Args&&... args) { /* ... */ } 

"bar"s是prvalue,並會匹配您的主要功能,因爲這會推斷,較好地一個const左值引用一個右值引用。

+0

或添加過載'爲const char *' – Sopel

+2

@ Rakete1111 http://coliru.stacked-crooked.com/a/6367292d4c72cd8c – Sopel

+1

嗯,不會'foo(「bar」s)'使第一個參數成爲右值,這仍然會導致第一個重載是首選? –

5
template <typename... Args> 
void foo(Args&&... args) { /* ... */ } 

麻煩,因爲你已經發現,就是這個函數是貪婪的,並且幾乎你扔在它的一切相匹配。如果參數類型與完全匹配 - 如果需要任何類型的轉換,那麼編譯器將傾向於實例化第一個模板。

最常用的解決方法是,如果第一個參數可以轉換爲std::string,我們可以使用標準類型特徵std::is_convertible進行測試,則使用SFINAE禁用第一個重載。通過這種方法,一對合適的重載是

// General case 
template <typename First, typename... Rest, 
      std::enable_if_t<!std::is_convertible<First, std::string>::value, int> = 0> 
void foo(First&& first, Rest&&... rest) { ... } 

// First argument can be converted to string 
template <typename... Args> 
void foo(const std::string& first, Args&&... args) { ... } 

Corilu link

+1

注意,如果我們正在接受與OP的重載相同的參數集,則您還需要一個'void foo()'重載來將調用與空參數列表進行匹配。 – cdhowie

+0

@cdhowie誠然,我忽略了那一個 –