C++ SFINAE 解析顺序

5
我有两个函数。
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 (&) []。我该怎么做?

2个回答

7
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


1
请注意,如果我们的目标是接受与 OP 的重载相同的参数集,则还需要一个 void foo() 重载来匹配空参数列表的调用。 - cdhowie
@cdhowie 是的,我忽略了那个。 - Tristan Brindle

6
问题在于"bar"不是一个std::string。无论怎样重新排列顺序,都不能调用void foo(const std::string& name, Args&&... args),因为这需要进行转换,而void foo(Args&&... args)将产生精确匹配。 一种解决方法是使用字面量字符串操作符并使用"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,而且会匹配您的主函数,因为它将推导出一个rvalue引用,这比const lvalue引用更受青睐。


const char* 添加重载函数 - Sopel
1
嗯,foo("bar"s) 不会使第一个参数成为右值吗?这样仍然会导致选择第一个重载函数吧? - Tristan Brindle
@TristanBrindle 您说得对。我忘记了它是一个const&而不是&&。现在应该可以工作了。 - NathanOliver
好的...这似乎能正常工作。我遇到了一些奇怪的行为。在我的原始代码中,如果我添加一个接受const char*的函数,那么一切都可以正常工作。然而,如果我将const std :: string&替换为const char *,它就无法正确解析。有什么想法? - ssb
@subzero 没有看到设置,我无法猜测。 - NathanOliver
@subzero 我想问题在于字符串字面值“bar”的类型不是const char*,而是const char[4],因此第一个重载仍然会被优先选择,因为不需要进行数组到指针的转换。 - Tristan Brindle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接