【问题标题】:Why is the "universal reference" overload being selected instead of the char array or char pointer?为什么选择“通用引用”重载而不是 char 数组或 char 指针?
【发布时间】:2014-07-23 07:33:56
【问题描述】:

我想解压可变参数模板包并根据包中的每种类型选择特定的重载。

我有 3 个用于解包各种类型的重载:

    // fixed size char arrays
    template<size_t N, typename... Ts>
    void unpack(const char (&)[N], Ts&&... ts);

    // char pointers
    template<typename... Ts>
    void unpack(const char*, Ts&&... ts);

    // all other types
    template<typename T, typename... Ts>
    void unpack(T&&, Ts&&... ts);

我将一个 char 数组 (char buf[1024]) 作为左值传递给可变参数构造函数。拆包时,T&amp;&amp;重载被选中。

如何选择const char (&amp;)[N]const char* 重载?

示例应用:

#include <iostream>

struct foo
{
    template<typename... Ts>
    foo(Ts&&... ts)
    {
        unpack(std::forward<Ts>(ts)...);
    }

    template<size_t N, typename... Ts>
    void unpack(const char (&)[N], Ts&&... ts)
    {
        std::cout << "const T (&)[N]" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    template<typename... Ts>
    void unpack(const char*, Ts&&... ts)
    {
        std::cout << "const char*" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    template<typename T, typename... Ts>
    void unpack(T&&, Ts&&... ts)
    {
        std::cout << "T&&" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    void unpack()
    {
    }
};

int main()
{
    char buf[1024];
    const char* str = "foo";
    foo f(buf, str);
    return 0;
}

上面的代码会打印以下内容:

T&&
const char*

我希望它打印:

const T (&)[N]
const char*

const char*
const char*

即:选择两个字符重载之一 - 数组或指针。

【问题讨论】:

  • @Jarod42 我试过了!正是上面的问题让我问了上一个问题……问题不在于 char 数组和 char 指针之间的歧义——它选择了T&amp;&amp; 重载。
  • 同样的解决方案也可能适用于这里:使用 SFINAE 来限制 T&amp;&amp; 捕获的内容或对类使用专业化。
  • @Jarod42 - 是的,谢谢 - 我是从 @user657267 的回答中得到的 - 事实上chars 不是 const

标签: c++ templates c++11 variadic-templates typetraits


【解决方案1】:

void unpack(T&amp;&amp;, Ts&amp;&amp;... ts) 被选中是因为 buf 数组中的 chars 不是 const

如果您将buf 更改为char const buf[1024]{};,编译器会告诉您调用不明确,这是朝着正确方向迈出的一步。从那里你可能需要使用 SFINAE(例如std::is_array)、std::decay 或其他一些方法来消除呼叫的歧义。

【讨论】:

  • 谢谢!我不知道 const 在重载决议中发挥了作用。
  • @SteveLorimer 确实,虽然顶级限定符没有,所以例如char const* const,最后的const 与解析目的无关。
  • @user657267 除非! f(char const* const&amp;) ;)
  • @R.MartinhoFernandes 由于没有 cv 限定引用这样的东西,并且该类型中的最终 const 指的是 *(即它不是顶级 cv 限定符),所以声明仍然成立。
【解决方案2】:

一种可能性是删除数组重载并直接使用

template<typename T, typename... Ts>
std::enable_if_t<std::is_same<std::decay_t<T>, char>::value> unpack(T*, Ts&&... ts)
{
    std::cout << "(const) char*" << std::endl;
    unpack(std::forward<Ts>(ts)...);
}

仅当T 被推断为(可能是 cv 合格的)char(这意味着它与 char *const char * 作为第一个参数的完美匹配)时才启用,在这些情况下它是比基本模板更专业,因此通过重载决议进行选择。

如果不使用 C++14,请将 enable_if_tdecay_t 替换为更详细的 typename /*...*/::type

Demo.

【讨论】:

    【解决方案3】:

    你可以使用类似的东西:

    template<typename T, typename... Ts>
    typename std::enable_if<!std::is_array<typename std::remove_reference<T>::type>::value>::type
    unpack(T&&, Ts&&... ts)
    {
        std::cout << "T&&" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }
    

    注意:目前禁止任何数组,而不仅仅是字符数组。

    【讨论】:

      【解决方案4】:

      根据您的需要,您可以检查是否与之前的重载匹配,而不是专门测试数组/指针:

      int unpack_helper(...);
      
      // fixed size char arrays
      template<size_t N, typename... Ts>
      void unpack_helper(const char (&)[N], Ts&&... ts);
      
      // char pointers
      template<typename... Ts>
      void unpack_helper(const char*, Ts&&... ts);
      
      // fixed size char arrays
      template<size_t N, typename... Ts>
      void unpack(const char (&)[N], Ts&&... ts);
      
      // char pointers
      template<typename... Ts>
      void unpack(const char*, Ts&&... ts);
      
      // all other types
      template<typename T, typename... Ts, size_t = sizeof(unpack_helper(std::declval<T&&>(), std::declval<Ts&&>()...))>
      void unpack(T&&, Ts&&... ts);
      

      这有一个可能的好处,也有一个可能的缺点,那就是使用转换运算符选择你之前的函数重载的类类型。

      【讨论】:

        猜你喜欢
        • 2016-11-08
        • 2011-08-10
        • 2019-01-09
        • 2018-04-05
        • 1970-01-01
        • 2021-01-26
        • 2017-09-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多