具有模板参数大小与char指针的Const char数组

时间:2014-10-01 10:58:51

标签: c++ templates pointers c++11

我今天在一些代码中看到了以下类型的结构:

template<unsigned int N> unsigned int f(const char (&a)[N]);

有没有任何合理的观点来解决这个问题:

unsigned int f(const char *a);

我模糊地理解后者的指针共享含义,但它真的很糟糕,它需要被一个两倍大小的模糊代码所取代吗?

(不幸的是,我不能问作者的代码,否则我会)

6 个答案:

答案 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)

使用它有两个原因:

  • 除了c风格的数组之外,不可能传递给该模板函数。因此,使用指针调用该方法将是编译错误。
  • 在模板函数中,您将获得在编译期间评估的数组大小

所以,这个:

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");

很好,你可以将这个数组的大小确定为编译常量