我今天在一些代码中看到了以下类型的结构:
template<unsigned int N> unsigned int f(const char (&a)[N]);
有没有任何合理的观点来解决这个问题:
unsigned int f(const char *a);
我模糊地理解后者的指针共享含义,但它真的很糟糕,它需要被一个两倍大小的模糊代码所取代吗?
(不幸的是,我不能问作者的代码,否则我会)
答案 0 :(得分:13)
将任何原始指针传递给函数的意图是调用者有一些想法:
作为输入参数的C样式字符串具有后者推断,因为假设通过到达null char终止符来认为“多少”。
但是,如果你不传递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
}
这一点并不少见,尽管如果调用者传给我们错误的长度(从不说永远不会),可能会出现错误。
但是如果有一种方法可以通过非类型模板参数使用演绎将实际变量 type 中的数据传递给函数呢?如果调用者使用固定数组调用我们该怎么办?
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)
固定长度的本机数组作为输入参数。
祝你好运。
答案 1 :(得分:3)
指针不会保留信息,无论它们指向单个对象还是数组的第一个对象。因此,如果您在函数体内写入
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);
考虑一种情况,当你需要将作为参数传递的字符数组附加到其他字符串时(让我们假设缺少限定符const)。当您使用第二个声明时,即使应用于指针的函数strlen
也无法帮助您确定原始字符串是否足够大以便可以追加。在第一种方法中,您可以使用表达式N - strlen( a )
来确定数组中是否有足够的空间。
答案 2 :(得分:3)
template<unsigned int N> unsigned int f(const char (&a)[N]);
此函数模板通过引用接收数组,这意味着当直接与静态分配的C-Arrays一起使用时,函数在编译时知道数组大小。
unsigned int f(const char *a);
这里所有函数都知道它已被赋予一些指向const char的指针。因此,如果您想使用它来操作数组,则必须将数组大小作为附加参数,因为无法仅从指针中检索此信息。
答案 3 :(得分:2)
模板版本采用一定大小的数组。指针版本只接受一个指针,没有指针所指向的数据大小的任何概念。
答案 4 :(得分:1)
为template
的每个不同值实例化了N
(因此生成了额外的机器代码),但在f
内,可以自由使用N
的知识来做什么是有用的(例如,从a
处理正确数量的条目,相应地调整它可能需要的另一个字符数组的大小)。您必须查看f
内部以了解N
是否实际使用,以及它是否有用。对于非模板版本,调用者不提供数组/数据大小,因此没有提供等效信息(如果数据a
指向的长度可变,那么f
将不得不依赖其他一些知道有多少数据的方法,例如strlen()
花时间搜索终止NUL。)
答案 5 :(得分:1)
使用它有两个原因:
所以,这个:
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");
很好,你可以将这个数组的大小确定为编译常量