问题 传递std:数组


我正在尝试编写一个可以在可变大小的std :: array上工作的函数,例如:

std::array a<int,5>={1,2,3,4,5};
std::array b<int,3>={6,7,8};

myFunc(a);
myFunc(b);

void myFunc(std::array &p)
{
cout << "array contains " << p.size() << "elements"; 
}

但是,除非我指定大小,否则它不起作用,但我希望函数从数组中获取大小。我真的不想要矢量使用的复制和动态分配,我想使用std :: array()分配的空间,并且不希望编译器为每个可能的数组大小创建函数的副本。

我想过创建一个像数组一样工作的模板,但是它会指向现有数据而不是自己分配它,但是不想重新发明轮子。

这有可能吗?


7322
2018-04-20 22:34


起源

如果您不想修改,则传递范围而不是容器 容器 (但只有元素,如果有的话)。 - dyp
顺便说一句。该 <int,5> 是的一部分 类型。这意味着a)你的前两行包含错别字和b)你不能只使用 模板  std::array 作为函数参数类型(因为它不是类型而是类型的模板)。 - dyp
@dyp修改a之间有什么区别 std::array 并修改一个元素 std::array? - aschepler
@aschepler这是一个更普遍的评论。将算法与容器分离是重要的IMO。 - dyp
“不希望编译器为每个可能的数组大小创建函数的副本” 对于参数中使用的每个可能的大小或每个大小 myFunc? - dyp


答案:


template<typename T, size_t N>
void myFunc(std::array<T, N> const &p) {
    cout << "array contains " << p.size() << "elements"; 
}

std::array 不是一个类,不能像一个类一样使用。它是一个模板,您必须实例化模板才能获得可以像类一样使用的类。

此外,大小是数组类型的一部分。如果你想要一个大小不属于该类型的容器,那么你可以使用类似的东西 std::vector。或者,您可以编写适用于具有所需功能的任何对象的模板:

template<typename Container>
void myFunc(Container const &p) {
    cout << "Container contains " << p.size() << "elements"; 
}

8
2018-04-20 22:42



最后的风格是pythonic;) - v.oddou


你真正想要的是类似于提议的std :: array_ref(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html)它接受任何大小,只包含指针范围(或指针和计数取决于它的实现方式),而不是每个可能的容器类型,堆分配的容器或每个调用点的迭代器对的模板膨胀。

人们忘记的另一个选择是std :: initializer_list,它实际上是array_ref,是一个轻量级的连续同类视图。虽然用于构造函数(名称'initializer'最有意义),但它们也可以被泛型函数使用。注意,传递时不会复制内容,只是指针范围,这意味着您不需要(也不应该)通过引用传递initializer_list。

它允许您直接在函数调用中传递一系列数字:

void myFunc(std::initializer_list<int> p)
{
    cout << "array contains " << p.size() << "elements"; 
}

myFunc({1, 2, 3, 4, 5});
myFunc({6, 7, 8});

或者通过中间变量:

std::initializer_list<int> a = {1, 2, 3, 4, 5};
std::initializer_list<int> b = {6, 7, 8};
auto c = {1, 2, 3, 4, 5};
auto d = {6, 7, 8};
myFunc(a);
myFunc(b);
myFunc(c);
myFunc(d);

甚至,它看起来是使用构造函数重载从固定大小的std :: array改编而来的,它带有一个开始/结束对。

std::array<int, 5> e = {1, 2, 3, 4, 5};
std::array<int, 3> f = {6, 7, 8};
myFunc(std::initializer_list<int>(e.data(), e.data() + e.size()));
myFunc(std::initializer_list<int>(f.data(), f.data() + f.size()));

虽然,我没有看到cppreference.com上和N4166中列出的这个构造函数重载。所以这个重载可能不是标准的,只能在Dinkumware的STL中找到(由Visual Studio 2015使用)。此外,没有来自数组/向量的隐式转换,无论如何都要最终编写中间帮助器模板函数(或者编写更多代码而不是简单地传递两个范围),这太糟糕了。理想情况下,std :: array将支持在标准中采用提升为initializer_list或array_ref的运算符方法。


3
2017-10-16 08:39





这是因为size是std :: array类型的一部分。

template< 
    class T, 
    std::size_t N 
> struct array;

不同大小的数组是不同的类型。你应该使用模板函数并写:

template< typename T, size_t N>
void myFunc( std::array<T, N> const &p) {
    std::cout << "array contains " << p.size() << "elements"; 
}

http://en.cppreference.com/w/cpp/container/array


1
2018-04-20 22:48





首选STL方式:将迭代器(按值)而不是容器传递给函数。 并模板化你的功能。

您的代码将与标准算法更好地集成,并且将与std :: array或其他标准容器同等地工作。

例如:

template<class InputIterator>
void myFunc (InputIterator first, InputIterator last);

1
2018-04-20 23:13



谢谢,编辑。 - quantdev


除了已经提出的建议之外,我想提出我的建议。

template<size_t N>
void myFunc(std::array<int, N> const &p) {
    cout << "array contains " << N << "elements"; 
}

顺便说一下,你的变量声明需要是:

std::array<int,5> a={1,2,3,4,5};  // <int, 5> needs to move to right after array.
std::array<int,3> b={6,7,8};

0
2018-04-20 23:20