C++11:初始化列表隐式转换为数组参数

6
在C++11中,是否可以做类似以下的事情?
template<typename T, size_t N>
void foo(array<T, N> src) { ... }

...

foo({1, 2, 3})

我目前正在运行GCC 4.8。

4个回答

7

是的,我成功地完成了以下工作(因为您允许类似的东西):

template<typename T, size_t N>
void foo(array<T, N> src) { ... }

...

foo('a', 'b');
foo(1, 2, 3);

这是如何做到的:
#include <array>
#include <iostream>
#include <utility>
using namespace std;

template<typename T, unsigned long N>
void foo(array<T,N> src) { 

  for (auto e : src)
    cout << e << endl;
}

template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
     std::array<T, 1 + sizeof...(Tail)> a = {{ head, tail ... }};
     return a;
}

template<class T, class... Tail> 
void foo(T&& head, Tail&&... values) {

    foo(make_array(std::forward<T>(head), std::forward<Tail>(values)...));
}

int main() {

  foo('a', 'b');

  foo(1, 2, 3);
}

我已经使用gcc 4.7.2和clang 3.4(trunk 184647)进行了测试,并且它们按预期工作。
这是一个在线版本在Stacked-Crooked上。但是,这段代码无法在Ideone上编译。由于我无法弄清楚在Ideone上传递给编译器的选项,因此我放弃了该站点。
我从@Pavel Minaev如何使用std::array模拟C数组初始化“int arr[] = { e1, e2, e3, … }”的行为?问题的回答中不要脸地偷了make_array函数。其他的make_array建议会导致编译错误,我无法修复。
这个make_array函数有限制,请阅读整篇文章;特别是comp.lang.c++.moderated上的讨论std::array——如果它只知道它的大小。显然,获得一个合理的make_array非常棘手。我不建议在生产代码中使用这个简单的make_array
如果大小是std::initializer_list的模板参数,则不会遇到任何问题。因此,提出了问题为什么大小不是std::initializer_list的模板参数?

不错啊... 我觉得我需要再读一些内容才能理解make_array函数... :) - Joachim Isaksson
为什么会有踩票?回答有什么问题吗?OP允许*类似的事情。 - Ali

3
显然不行。标准(14.8.2.5)将其称为非推导上下文;
但在某些情况下,该值不参与类型推断,而是使用模板参数的值,在其他地方已被推导或明确指定。
不推断的上下文包括:
函数参数,其相关联的参数是初始化列表(8.5.4),但参数没有std::initializer_list或对可能具有cv限定符的std::initializer_list类型的引用。
例如: template void g(T); g({1,2,3}); // 错误:未推导出T的参数
编辑:如果只使用initializer_list重载来进行类型推断,您也可以使用std::vector使相同的功能正常工作;
template<typename T>
  void foo(const std::vector<T>& src) { ...your code here... }
template<typename T>
  void foo(const std::initializer_list<T>& src) { foo(std::vector<T>(src)); }

foo({1,2,3});  // Compiles

但遗憾的是,由于initializer_list的大小不是模板参数,我想不到一种方法来使其推导并将数组大小initializer_list中以与类型相同的方式向前传递。


向量解决方案似乎可以在没有初始化列表重载的情况下工作。 - jameszhao00
@jameszhao00 你使用的是哪个gcc版本?我只有4.7.3版本可以进行测试。编辑:啊,4.8版本,没看见你是提出这个问题的人 :) - Joachim Isaksson

2

您可以直接使用初始化列表来实现这种语法。例如:

#include <iostream>
#include <initializer_list>

void foo(std::initializer_list<int> il) {
  for (auto i: il)
    std::cout << i < std::endl;
}

int main() {
  foo({1,2,3});
}

或者使其更通用:
template <typename T>
void foo(std::initializer_list<T> il) {
  ...

更进一步,为什么不将 foo 设计成适用于任何容器的模板呢? - user4945014

1
可以使用原始数组引用实现:
template <typename T, size_t N>
void foo(T const (&x)[N]) {
    // x is [1, 2, 3], N = 3
}

int main() {
    foo({1, 2, 3});
    return 0;
}


请注意,数组必须声明为const。

这很好。你有没有想过为什么const允许这种隐式转换?如果有的话,请分享一下。 - namezero
是因为引用必须是const吗? - namezero
1
我不知道。我只是在《C++ Templates》这本书中读到的。 - Stefan Dragnev
我相信我有同样的书。里面有很多关于数组和指针衰减的内容。你记得是哪一章节吗? - namezero
1
第二版的5.4和7.4节,我想。 - Stefan Dragnev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接