【问题标题】:C++ SFINAE Resolution OrderC++ SFINAE 解析顺序
【发布时间】:2017-08-04 15:35:39
【问题描述】:

我有两个功能

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&amp;const char (&amp;) []。我该怎么做?

【问题讨论】:

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


    【解决方案1】:

    这里的问题是"bar" 不是std::string。没有多少重新排序会调用 void foo(const std::string&amp; name, Args&amp;&amp;... args),因为这需要转换,而 void foo(Args&amp;&amp;... args) 将产生完全匹配。

    您可以使用literal string operator 并使用"bar"s"bar" 设为字符串的一种解决方法。这确实需要改变

    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 是一个纯右值,并且会匹配您的主函数,因为这会推断出比 const 左值引用更可取的右值引用。

    【讨论】:

    • 或为const char*添加重载
    • 嗯,foo("bar"s) 不会将第一个参数设为右值,这仍会导致首选第一个重载吗?
    • @TristanBrindle 你的权利。我忘了它是const&amp; 而不是&amp;&amp;。现在应该可以工作了。
    • 好的...这似乎有效。我有一些奇怪的行为。在我的原始代码中,如果我添加一个接受const char* 的函数,那么一切正常。但是,如果我将 const std::string&amp; 替换为 const char*,它就不再正确解析。知道这里发生了什么吗?
    • @subzero 没有看到我无法猜测的设置。
    【解决方案2】:
    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

    【讨论】:

    • 请注意,如果我们要接受与 OP 重载相同的参数集,您还需要一个 void foo() 重载来匹配具有空参数列表的调用。
    • @cdhowie 没错,我忽略了那个
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    相关资源
    最近更新 更多