2017-08-04 80 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诚然,我忽略了那一个 –