C ++ SFINAE解决方案订单

时间:2017-08-04 15:35:39

标签: c++ c++14 sfinae variadic-functions enable-if

我有两个功能

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检查char数组/字符串,因为Args...包可能包含std::string&const char (&) []。我该怎么做?

2 个答案:

答案 0 :(得分: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并且会匹配你的main函数,因为它会推导出一个rvalue引用,它优于const lvalue引用。

答案 1 :(得分:6)

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

正如您所发现的那样,问题在于这个功能很贪婪,并且几乎可以匹配您投入的所有内容。编译器更喜欢另一个重载的唯一情况是,如果参数类型与完全匹配 - 如果需要任何类型的转换,则编译器将优先实例化第一个模板。

解决此问题的最常用方法是使用SFINAE来禁用第一个重载,如果第一个参数可以转换为std::string,我们可以使用标准类型特征std::is_convertible进行测试。使用这种方法,一对合适的过载将是

// 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