我今天在一些代码中看到了以下类型的结构:
template<unsigned int N> unsigned int f(const char (&a)[N]);
这个和下面这个相比,有什么合理的优点吗:
unsigned int f(const char *a);
我对后面的指针共享影响有点理解,但它真的如此糟糕,以至于需要用两倍大小的晦涩代码来替换吗?
(不幸的是,我无法询问代码的作者,否则我会这样做)
将任何裸指针传递给函数的目的是为了调用方能够对以下内容有所了解:
作为输入参数的C风格字符串,后者是可以推断出来的,因为假设"多少个"是通过到达空字符终止符来确定的。
但是如果您没有传递C风格字符串呢?如果它只是一个零个或更多char
值的序列呢?那么,如果是这种情况,那么:
void f(const char *s)
{
// how many char does s refer to?
}
逻辑推断应该是这样做:
void f(const char *s, std::size_t N)
{
// Now the caller is telling us there are N chars at s
}
这种情况并不罕见,但如果调用者传递了错误的长度(永远不要说永远),那么可能会出现错误。
但是,如果有一种方法可以通过非类型模板参数的推导从传递给函数的变量类型中吃掉该数据呢? 如果调用者正在使用固定数组来调用我们,会怎样呢?
template<std::size_t N>
void f(const char(&ar)[N])
{
// we know the caller is passing a const-reference to a
// char array declared of size N. The value N can be used
// in this function.
}
现在我们知道列表中的两个项目: "什么" 和 "多少"。此外,我们现在可以提供模板函数和重载,并且两者都可用于我们:
// length specified implementation
void f(const char *s, std::size_t N)
{
// caller passed N
}
// fixed buffer template wrapper
template<std::size_t N>
void f(const char(&ar)[N])
{
f(ar,N); // invokes length-specified implementation from above.
}
以下两者都能正常工作:
int main()
{
char buff[3];
f(buff,3);
f(buff);
}
那么这有什么好处呢?因为以下代码将会发生编译错误,因为找不到匹配的实现:
int main()
{
char buff[3];
const char *ptr = buff;
f(ptr); // ERROR: no matching function f(const char *)
}
总的来说,这是一种常见的技术,可以帮助我们向调用者提供列表中的两个项目:"是什么"和"多少",而不必每次在使用固定长度本地数组作为输入参数时都手写sizeof(ar)/sizeof(*ar)
。
祝你好运。
unsigned int f(const char *a);
表达式
sizeof( a ) / sizeof( *a )
如果您在函数体内使用相同的表达式,您将仅获得指针本身的大小。
template<unsigned int N> unsigned int f(const char (&a)[N]);
你将得到数组的大小(当然,你也可以使用 N 的值)。
因此,当使用第二种方法时,通常会声明带有两个参数的函数,其中第二个参数指定数组的大小。
unsigned int f(const char *a, size_t n);
template<unsigned int N> unsigned int f(const char (&a)[N]);
这个函数模板通过引用接收数组,这意味着当直接与静态分配的C数组一起使用时,函数在编译时知道数组大小。
unsigned int f(const char *a);
这里所有的函数都知道的是它们被给了一个指向const char的指针。因此,如果您想将其用于操纵数组,则必须额外提供数组大小作为参数,因为无法仅从指针中检索此信息。
模板版本接受一定大小的数组。指针版本仅接受指针,没有关于指针所指数据大小的概念。
template
会为每个不同的N
值实例化(因此会生成额外的机器码),但在f
内部,它可以自由地使用它对N
的知识来执行任何有用的操作(例如,从a
中处理正确数量的条目,相应地调整另一个可能需要的字符数组的大小)。要知道N
是否实际被使用以及是否有用,必须查看f
的内部。非模板版本中,调用者不提供数组/数据大小,因此没有提供等效信息(如果数据a
指向的长度是可变的,则f
必须依赖其他方式来知道有多少数据,例如,strlen()
花费时间搜索终止NUL)。N
值将有意义,无需使用strlen
等函数(即使有时仅提供有用的上限)。 - Tony Delroy使用它的两个原因是:
因此,这个:
const char* p="abc";
f(p);
garbage.cpp:39:5: error: no matching function for call to ‘f(const char*&)’
f(p);
^
garbage.cpp:39:5: note: candidate is:
garbage.cpp:21:39: note: template<unsigned int N> unsigned int f(const char (&)[N])
template<unsigned int N> unsigned int f(const char (&a)[N])
而这个:
f("abc");
这是可以的,你可以将此数组的大小确定为编译常量
a
指向什么?一个单独的int
?一个简单的数组?一个 C 字符串?一个以 null 结尾的数组?明白我的意思了吗? - Shoe