防止参数包扩展中的数组衰减

Nat*_*ohl 9 c++ templates c++11

是否可以防止从参数包扩展的参数中的数组到指针衰减?

例如:

#include <iostream>

void foo() {
  std::cout << "empty\n";
}

template <typename T, typename... Rest>
void foo(T &&t, Rest... rest) {
  std::cout << "T, ...\n";
  foo(rest...);
}

template <typename... Rest>
void foo(char *p, Rest... rest) {
  std::cout << "char*, ...\n";
  foo(rest...);
}

template <int N, typename... Rest>
void foo(char (&first)[N], Rest... rest) {
  std::cout << "char[], ...\n";
  foo(rest...);
}

int main() {
  char a[2], b[2], c[2];
  foo(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)

...输出:

char[], ...
char*, ...
char*, ...
empty
Run Code Online (Sandbox Code Playgroud)

如您所见,第一次调用转到基于数组的重载,但后续调用转到基于指针的重载. 有没有办法让所有的调用转到基于数组的重载?

相关:专门针对变量模板功能的问题

Jer*_*fin 8

您想通过rvalue引用传递参数包:

void foo(char (&first)[N], Rest&&... rest)
                               ^^
Run Code Online (Sandbox Code Playgroud)

所以代码整体看起来像这样:

#include <iostream>

void foo() {
  std::cout << "empty\n";
}

template <typename T, typename... Rest>
void foo(T &&t, Rest... rest) {
  std::cout << "T, ...\n";
  foo(rest...);
}

template <typename... Rest>
void foo(char *p, Rest... rest) {
  std::cout << "char*, ...\n";
  foo(rest...);
}

template <int N, typename... Rest>
void foo(char (&first)[N], Rest&&... rest) {
  std::cout << "char[], ...\n";
  foo(rest...);
}

int main() {
  char a[2], b[2], c[2];
  foo(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)

给出结果:

char[], ...
char[], ...
char[], ...
empty
Run Code Online (Sandbox Code Playgroud)

我没有改变其他重载来做同样的事情,但你通常也希望它们也使用右值引用(如果实际使用它们).

编辑:至于你为什么要这样做/为什么它起作用:右值引用可以绑定到右值或右值.我们关心的关键点是当它与左值结合时,它仍然是左值.在数组的情况下,它将其身份保留为数组,因此接收的是数组.

当/如果我们按值传递数组时,它会经常正常地"衰减"到指针,就像使用普通函数一样.

对于这个特定的情况下,我们也可以用一个正常的左值参考-但如果我们这样做,会不会对任何类型的,这不是一个左值工作.例如,如果我们试图调用foo(1,2,3);,我们会收到错误,因为左值引用无法绑定1,23.为了解决这个问题,我们可以传递一个const左值引用,但是我们不会直接将引用绑定到rvalue - 我们将创建一个临时包含传递的rvalue的副本,然后绑定左值引用而是临时副本.对于int的特定情况,这可能不是一个主要问题,但复制成本更高(或者如果我们想要访问原始文件,而不是副本)可能是一个问题.


did*_*erc 5

@JerryCoffin的答案已经到了现场,但我想补充一点.您可以将列表处理代码与第一项分开,如下所示:

void foo_list() {
   std::cout << "empty\n";
}

template <typename T, typename... Rest>
void foo_list(T &&t, Rest&&... rest) {
  foo(t);
  foo_list(rest...);
}

template <int N>
void foo(char (&t)[N]){
   // ...
}

void foo(char *){
   // ...
}

// etc...
Run Code Online (Sandbox Code Playgroud)

(也许已经有了这个成语?).