9

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

例如:

#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);
}

...输出:

char[], ...
char*, ...
char*, ...
empty

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

相关:变量模板函数的问题

4

2 回答 2

8

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

void foo(char (&first)[N], Rest&&... rest)
                               ^^

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

#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);
}

给出结果:

char[], ...
char[], ...
char[], ...
empty

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

编辑:关于你为什么要这样做/为什么它起作用:右值引用可以绑定到右值或左值。我们在这里关心的关键点是,当它绑定到左值时,它仍然是左值。在数组的情况下,它作为数组保留了它的身份,所以接收的是一个数组。

当/如果我们按值传递数组时,它会像普通函数一样经历正常的“衰减”到指针。

对于这种特殊情况,我们也可以使用普通的左值引用——但如果我们这样做了,那将不适用于任何不是左值的类型。例如,如果我们试图调用foo(1,2,3);,我们会得到一个错误,因为左值引用不能绑定到1,23。为了解决这个问题,我们可以传递一个const左值引用,但是我们不会将引用直接绑定到右值——我们会创建一个包含传递的右值副本的临时对象,然后绑定左值引用改为该临时副本。对于 int 的特定情况,这可能不是主要问题,但是对于复制成本更高的东西(或者如果我们想要访问原始文件,而不是副本),这可能是一个问题。

于 2012-12-17T21:13:14.917 回答
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...

(也许已经有一个成语了?)。

于 2012-12-17T21:39:13.640 回答