C++ SFINAE解决方案订单

ssb*_*ssb 4 c++ variadic-functions sfinae enable-if c++14

我有两个功能

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

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

目前所有的调用都foo("bar", /* arguments */)试图转到第一个函数而不是第二个函数.我想重新排序这些函数,以便SFINAE在第一个函数之前找到第二个函数.我不能std::enable_if用来检查char数组/字符串,因为Args...包可能包含std::string&或const char (&) [].我该怎么做呢?

Nat*_*ica 6

这里的问题"bar"不是一个问题std::string.不会void foo(const std::string& name, Args&&... args)调用任何数量的重新排序,因为这将需要转换,同时void foo(Args&&... args)将产生完全匹配.

一种解决方法是,您可以使用文字字符串运算符并"bar"使用字符串"bar"s.这确实需要改变

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

成

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

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

因为它"bar"s是一个prvalue并且会匹配你的main函数,因为它会推导出一个rvalue引用,它比const lvalue引用更受欢迎.


Tri*_*dle 6

template <typename... Args>
void foo(Args&&... args) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

正如你所发现的那样,问题在于这个函数是贪婪的,几乎可以匹配你抛出的所有东西.编译器更喜欢另一个重载的唯一情况是,如果参数类型完全匹配- 如果需要任何类型的转换,则编译器将优先实例化第一个模板.

解决此问题的最常用方法是使用SFINAE来禁用第一个重载,如果第一个参数可以转换为a 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) { ... }
Run Code Online (Sandbox Code Playgroud)

Corilu链接