【问题标题】:pass reference to array in C++在 C++ 中传递对数组的引用
【发布时间】:2011-03-02 21:07:49
【问题描述】:

谁能帮我理解下面的代码

#include <iostream>

void foo(const char * c)
{
   std::cout << "const char *" << std::endl;
}

template <size_t N>
void foo(const char (&t) [N])
{
   std::cout << "array ref" << std::endl;
   std::cout << sizeof(t) << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);

    char d[34] = {'1'};
    foo(d);
}

输出是

const char *
array ref
34

为什么第一个 foo 调用 const char * 版本?我怎样才能让它调用参考版本?

【问题讨论】:

  • @user511274 - 有趣的问题:)
  • 啊,没有什么能比得上模棱两可的重载解析(又名 Koenig 查找)。一般来说,当给定一个以上的函数选择时,模板函数是最后选择的。
  • 恭喜,你已经走到了 C++ 的尽头!
  • @Loadmaster:次要的挑剔,但 Koenig 查找是确定要考虑哪些功能的过程。然后重载决议是确定使用哪一个的以下过程。如果代码显示::foo(t),则不会涉及 Koenig 查找,但会涉及重载解析。
  • @junjanes No. @Marlon 的编译器错误。

标签: c++


【解决方案1】:

const char[N]const char* 的转换被认为是“完全匹配”(主要是为了使文字更容易),并且在两个完全匹配之间,非模板函数优先。

你可以使用enable_ifis_array 来强制它做你想做的事情。


强制它的一种混乱方式可能是:

#include <iostream>

template <typename T>
void foo(const T* c)
{
   std::cout << "const T*" << std::endl;
}

template <typename T, size_t N>
void foo(const T (&t) [N])
{
   std::cout << "array ref" << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);

    char d[34] = {'1'};
    foo(d);
}

/*
array ref
array ref
*/

我意识到 OP 有 char 而不是一些通用的 T,但这表明问题在于一个重载是模板而不是另一个。

【讨论】:

  • 第二次调用foo()第二版怎么解释?
  • @otibom:char[N] 的转换没有相同的规则。 char[N]const char* 不是完全匹配:它是可转换的,但模板函数更容易匹配。
  • @otibom 第一个版本不是 const,但模板版本采用 const 数组指针。
  • @otibom: 从char[34]char* 或到const char (&amp;) [34] 的转换是“完全匹配”,但是从char[34]const char* 的转换是指针转换,它确实使重载的区别。
  • @aschepler 为什么从char[34]const char (&amp;)[34] 的转换被认为是“完全匹配”?能详细解释一下吗?
【解决方案2】:

让我们看看这个没有模板的修改示例。

void foo(const char * c)
{
    std::cout << "const char *" << std::endl;
}

void foo(const char (&t) [34])
{
    std::cout << "const char (&) [34]" << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);
}

我的编译器说重载foo 的调用不明确。这是因为从数组到指针的转换被认为是“精确”转换序列,并且不比重载解析的空转换序列更好(标准第 13.3.3.1.1 节。)

在原始代码中,模板参数N可以推导出为34,但是在重载解析中同时考虑了非模板foo(const char*)foo&lt;34&gt;(const char (&amp;)[34])。由于转换规则两者都不比另一个更好,因此非模板函数优于模板函数。

解决问题似乎很棘手。似乎来自标头 &lt;type_traits&gt;is_array 模板(如果可能,来自 C++0x,否则来自 Boost)可能会有所帮助。

【讨论】:

  • 确实,你可以使用 enable_if 和 is_array 来得到你想要的。
  • @Tomalak:有没有使用 C++03 和 Boost 的简单方法?我打算举个例子,然后意识到我绝对需要右值引用,它不是问题标签中语言的一部分。
  • @aschepler:我用一种涉及公平竞争环境的方法更新了我的答案。
  • @aschepler:(其实和你上面的代码差不多,但是方向相反)
  • 除了我的代码仅用于解释目的,不能解决任何问题。我仍然对仅针对 cv-char 类型的指针/数组进行操作感到好奇。也许template &lt;typename T&gt; void foo(const T* c, enable_if&lt;is_same&lt;T,char&gt; &gt;* = 0);
【解决方案3】:

这对于不同的编译器来说似乎是不同的。

Mircosoft 和 Borland 都使用 const char* 版本,而 GNU 提供您描述的输出。

这是来自 C++ 标准的 sn-p:

14.8.2.1 从函数调用中推导出模板参数 [temp.deduct.call]

模板参数推导由 比较每个功能模板 参数类型(称为 P)与 的相应参数的类型 所描述的呼叫(称为 A) 下面。

如果 P 不是引用类型:

--如果A是数组类型,则数组到指针产生的指针类型 标准转换(4.2)用于 类型扣减A的地方; 否则,

-- 如果 A 是函数类型,则由 函数指针标准 转换 (4.3) 用于代替 A 用于类型扣除;否则,

-- 如果 A 是 cv 限定类型,则 A 类型的顶级 cv 限定符 类型推导被忽略。

如果 P 是 cv 限定类型,则顶部 P 类型的级别 cv 限定符是 类型推导被忽略。如果 P 是一个 引用类型,引用的类型 by P 用于类型推导

编译器将构建一个A 列表,如下所示:

Argument:        t                 d
A:          char const[34]      char[34]

和参数列表P

Parameter:       c                 t
P:            char const*       char const& t[N]

默认情况下,编译器应该选择非引用参数。由于某种原因,GNU 第二次犯错了。

【讨论】:

  • 不,不是。您忘记了首选非模板。如果 MS 和 Borland 给出不同的输出,那么它们是不合规的。模板类型推导完全无关。
  • 模板推导论点不是问题。重载分辨率是。
猜你喜欢
  • 2013-04-02
  • 2010-09-07
  • 2016-06-06
  • 2013-11-07
  • 2010-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多