简体   繁体   English

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

[英]Const char array with template argument size vs. char pointer

I saw a construct of the following type in some code today: 我今天在一些代码中看到了以下类型的结构:

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

Is there any reasonable point in having this over having: 有没有任何合理的观点来解决这个问题:

unsigned int f(const char *a);

I vaguely understand the pointer sharing implications of the latter, but is it really so bad, that it needs to be replaced by an obscure code twice the size? 我模糊地理解后者的指针共享含义,但实际上是非常糟糕的,它需要被两倍大小的模糊代码所取代吗?

(Unfortunately, I cannot ask the author of the code, otherwise I would) (不幸的是,我不能问作者的代码,否则我会)

The intent of passing any raw pointer to a function is the caller has some idea of: 将任何原始指针传递给函数的意图是调用者有一些想法:

  • What it points to. 它指向的是什么。
  • How many it points to. 有多少指向。

C-style strings as input parameters have the latter inferred, as the assumption is that "how many" is deemed by arrival at the null char terminator. 作为输入参数的C样式字符串具有后者推断,因为假设通过到达null char终止符来认为“多少”。

But what if you're not passing a C-style string? 但是,如果你没有传递C风格的字符串怎么办? What if it is simply a sequence of zero-or-more char values? 如果它只是一个零或多个char值的序列怎么办? Well, if that is the case, then: 好吧,如果是这样的话,那么:

void f(const char *s)
{
    // how many char does s refer to?
}

The logical deduction would be to do this: 逻辑演绎将是这样做的:

void f(const char *s, std::size_t N)
{
    // Now the caller is telling us there are N chars at s
}

and this is not uncommon at all, albeit a potential point of error if the caller passes us the wrong length (never say never). 这种情况并不少见,尽管如果调用者传给我们错误的长度(从不说永远),可能会出现错误。

But what if there were a way to slurp that data from the actual variable type being passed to the function using deduction via non-type template parameter? 但是如果有一种方法可以通过非类型模板参数使用演绎将实际变量类型的数据传递给函数呢? What if the caller is invoking us with a fixed array? 如果调用者使用固定数组调用我们该怎么办?

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.
}

Now we know both items in our list: the "what" and the "how many". 现在我们知道列表中的两个项目:“什么”和“多少”。 Furthermore, we can now provide both a template function and an overload and have both worlds available to us: 此外,我们现在可以同时提供一个模板函数和过载 ,并有提供给我们的两个世界:

// 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.
}

And both of the following will work: 以下两种方法都有效:

int main()
{
    char buff[3];

    f(buff,3);
    f(buff);

}

So how is this good? 那怎么这么好? Because the following will flag a compiler error, as no matching implementation can be found: 因为以下将标记编译器错误,因为找不到匹配的实现:

int main()
{
    char buff[3];
    const char *ptr = buff;
    f(ptr); // ERROR: no matching function f(const char *)
}

In summary it is a common technique to assist in providing both items in our bullet list to the callee: the "what" and the "how much", without having to long-hand sizeof(ar)/sizeof(*ar) every time you use a fixed-length native array as your input parameter. 总之,这是一种常见的技术,可以帮助我们将子弹列表中的项目提供给被调用者:“what”和“多少”,而不必每次都使用sizeof(ar)/sizeof(*ar)您使用固定长度的本机数组作为输入参数。

Best of luck. 祝你好运。

Pointers do not keep information whether they point to a single object or the first object of an array. 无论指针指向单个对象还是数组的第一个对象,指针都不会保留信息。 So if you for example write within the body of function 所以如果你在函数体内写的话

unsigned int f(const char *a);

expression 表达

sizeof( a ) / sizeof( *a )

you will get only the size of the pointer itself. 你将只得到指针本身的大小。 While if you would use the same expression within the body of function 如果你在函数体内使用相同的表达式

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

you will get the size of the array (of course you could use simply value of N). 你将获得数组的大小(当然你可以使用简单的N值)。

So when the second approach is used then usually such functions are declared with two parameters where the second parameter specifies the size of the array 因此,当使用第二种方法时,通常使用两个参数声明此类函数,其中第二个参数指定数组的大小

unsigned int f(const char *a, size_t n);

Consider a situation when you need to append the character array passed as an argument with other string (let's assume that qualifier const is absent). 考虑一种情况,当你需要将作为参数传递的字符数组附加到其他字符串时(让我们假设缺少限定符const)。 When you use the second declaration then even function strlen applied to the pointer will not help you to determine whether the original string is large enough that could be appended. 当您使用第二个声明时,即使应用于指针的函数strlen也无法帮助您确定原始字符串是否足够大以便可以追加。 In the first approach you can use expression N - strlen( a ) that to determine whether there is enough space in the array. 在第一种方法中,您可以使用表达式N - strlen( a )来确定数组中是否有足够的空间。

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

This function template takes in the array by reference, which means when used directly with statically allocated C-Arrays the function knows the array size at compile. 此函数模板通过引用接收数组,这意味着当直接与静态分配的C-Arrays一起使用时,函数在编译时知道数组大小。

unsigned int f(const char *a);

Here all the function knows is that it has been given some pointer to a const char. 这里所有函数都知道它已被赋予一些指向const char的指针。 So if you wanted to use it to manipulate an array it would have to take in the array size as an additional argument since there is no way to retrieve this information from the pointer alone. 因此,如果您想使用它来操作数组,则必须将数组大小作为附加参数,因为无法仅从指针中检索此信息。

The template version takes an array of a certain size. 模板版本采用特定大小的数组。 The pointer version takes only a pointer, without any concept of the size of the data pointed to by the pointer. 指针版本只接受一个指针,没有指针所指向的数据大小的任何概念。

The template in instantiated for each distinct value of N (so there's extra machine code generated) but inside f it's free to use its knowledge of N to do whatever's useful (eg process the right number of entries from a , correspondingly size another character array it might need). template实例化为N每个不同的值(因此生成了额外的机器代码)但在f内部可以自由地使用其N知识来做任何有用的事情(例如,从a处理正确数量的条目,相应地调整另一个字符数组的大小)可能需要)。 You have to look inside f to know whether N is actually used, and if it's useful. 您必须查看f内部以了解N是否实际使用,以及它是否有用。 With the non-template version the caller's not providing an array/data size, so there's no equivalent information provided (if the data a points to is variable in length then f would have to rely on some other way to know how much data there is, eg strlen() taking the time to search for a terminating NUL). 与非模板版本的来电者不是提供一个数组/数据大小,所以有没有提供相同的信息(如果该数据a点的长度是可变那么f将不得不依靠一些其他的方式来知道有多少数据是例如strlen()花时间搜索终止NUL)。

There are two reasons to use that : 使用它有两个原因:

  • it is not possible to pass to that template function anything but c-style arrays. 除了c风格的数组之外,不可能传递给该模板函数。 So, calling that method with a pointer would be a compilation error. 因此,使用指针调用该方法将是编译错误。
  • in the template function you get the size of the array, evaluated during the compilation 在模板函数中,您可以获得在编译期间评估的数组大小

So, this : 所以这 :

const char* p="abc";
f(p);

causes the compilation to fail : 导致编译失败:

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])

Whereas, this : 然而,这个:

f("abc");

is fine, and you get the size of this array determined as a compilation constant 很好,你得到这个数组的大小确定为编译常量

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM