在C ++中传递对数组的引用
任何人都可以帮我理解下面的代码
#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 *
版本? 我怎样才能叫它的参考版本?
将const char[N]
转换为const char*
被认为是“完全匹配”(主要是为了使文字更容易),并且在两个完全匹配之间,非模板函数优先。
你可以使用enable_if
和is_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
不是一个genericsT
,但是这表明问题在于一个超载是模板而不是另一个。
让我们看看这个没有模板的修改示例。
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<34>(const char (&)[34])
。 由于转换规则既不比另一个更好,非模板函数也会跳过模板函数。
修复事情似乎很棘手。 看起来好像来自头<type_traits>
的is_array
模板(如果可能的话,从C ++ 0x开始,或者如果没有的话,Boost)可能会有所帮助。
这对于各种编译器来说似乎是不同的。
Mircosoft和Borland都使用const char *版本,而GNU则给出你描述的输出。
这里是C ++标准的片段:
14.8.2.1从函数调用中推导模板参数[temp.deduct.call]
通过比较每个函数模板参数types(称为P)和调用的相应参数的types(称为A)来完成模板参数推导,如下所述。
如果P不是参考types:
– 如果A是数组types,则使用由数组到指针标准转换(4.2)生成的指针types来代替A来进行types推导; 除此以外,
– 如果A是一个函数types,则使用函数对指针标准转换(4.3)生成的指针types来代替A来进行types推导; 除此以外,
– 如果A是cv限定types,Atypes的顶级cv限定符将被忽略。
如果P是一个cv限定types,那么Ptypes的顶级cv限定符将被忽略。 如果P是参考types,则P所指的types用于types推导
编译器将build立一个列表如下:
Argument: td A: char const[34] char[34]
和参数列表P
:
Parameter: ct P: char const* char const& t[N]
默认情况下,编译器应该select未引用的参数。 由于某种原因,GNU第二次出错了。