如何在结构体中迭代向量?

3

我正在为学校创建一种数字售货机,并遇到了一个问题。我创建了一个名为Items的结构体用于售货。然后,我创建了一个名为Machine的结构体,其中包含一个vector<Items>。我想创建一个for循环,遍历vector<Item>并显示物品,但是我收到以下错误:

C:\Users\Nate\Desktop>g++ structversion.cpp -o structversion.exe -std=c++11
structversion.cpp: In function 'int test(Machine)':
structversion.cpp:29:20: error: 'begin' was not declared in this scope
   for (Item item : machine) {
                    ^
structversion.cpp:29:20: note: suggested alternatives:
In file included from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/string:51:0,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/locale_classes.h:40,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/ios_base.h:41,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/ios:42,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/ostream:38,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/iostream:39,
                 from structversion.cpp:1:
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/range_access.h:87:5: note:   'std::begin'
     begin(_Tp (&__arr)[_Nm])
     ^
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/range_access.h:87:5: note:   'std::begin'
structversion.cpp:29:20: error: 'end' was not declared in this scope
   for (Item item : machine) {
                    ^
structversion.cpp:29:20: note: suggested alternatives:
In file included from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/string:51:0,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/locale_classes.h:40,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/ios_base.h:41,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/ios:42,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/ostream:38,
                 from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/iostream:39,
                 from structversion.cpp:1:
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/range_access.h:97:5: note:   'std::end'
     end(_Tp (&__arr)[_Nm])
     ^
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/bits/range_access.h:97:5: note:   'std::end'

如果这个问题是多余或愚蠢的,请原谅。以下是有问题的代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct Item 
{
    string name;
    double price;
    unsigned int quantity;
    unsigned int amountInCart;
    bool addedToCart;
};

struct Machine { vector<Item> menu; };

void initItem(Item& i, string name, double price, unsigned int quantity,
    unsigned int amountInCart, bool addedToCart) 
{
    i.name = name;
    i.price = price;
    i.quantity = quantity;
    i.amountInCart = amountInCart;
    i.addedToCart = addedToCart;
}

test(Machine machine)
{
    for (Item i : machine) {
        cout << "item = " << i.name;
    }
}

main()
{
    Item cake;
    Item fruit;
    Item chips;
    Item soda;
    Item juice;

    initItem(cake, "Cake", 3.00, 5, 0, false);
    initItem(fruit, "Fruit", 4.20, 15, 0, false);
    initItem(chips, "Chips", 1.00, 6, 0, false);
    initItem(soda, "Soda", 1.50, 7, 0, false);
    initItem(juice, "Juice", 1.90, 10, 0, false);

    Machine machine;
    machine.menu.push_back(cake);
    machine.menu.push_back(fruit);
    machine.menu.push_back(chips);
    machine.menu.push_back(soda);
    machine.menu.push_back(juice);

    test(machine);
    return 0;
}

函数test是我尝试在Machine结构中迭代向量菜单的地方。

我还是新手,如果有人有时间并且能够以儿童语言解释我做错了什么,那将是很棒的。


希望这只是复制粘贴时的笔误:你的 testmain 函数缺少了返回语句。 - JeJo
哦,是的,没错!这只是我从我的主要项目中提取的破损代码片段。我只想包括相关的代码。再次感谢您的帮助。 - cappyt
你应该学习什么是构造函数,不要使用晦涩的init函数!此外,在这种情况下,你可以使用聚合初始化。 - Klaus
1个回答

5
为了在用户定义的类型中使用基于范围的for循环,您需要定义begin()end()迭代器。这意味着Machine需要具有以下成员函数,它们返回成员容器的迭代器。(在线查看)
struct Machine
{
    vector<Item> menu;
    auto begin() { return menu.begin(); } // auto return requires C++14
    auto end()   { return menu.end();   }

    // or in C++11, you provide the correct return type
    // decltype(menu.begin()) begin() { return menu.begin(); }
    // decltype(menu.end())   end()   { return menu.end();   }
};

否则,您需要直接将迭代对象提供给循环。这意味着,在您的情况下,为了能够进行最小更改的工作:
void test(Machine const& machine) // pass by `const-ref` as the machine is read-only inside the function
{
    for (const Item& i : machine.menu) { // const& : same reason as above ^^
    //                   ^^^^^^^^^^^^ --> std::vector has iterators: begin(), end() 
        cout << "item = " << i.name;
    }
}

一些其他的注释:
  • The initItem function is not the way you do in normally in C++ (or any other object-oriented programming languages). That is the job of a constructor. In order to change the value of the member(s) in an object, the setter (member) function will be used. A good starting would be:

    class Item
    {
    private:
        std::string name;
        double price;
        unsigned int quantity;
        unsigned int amountInCart;
        bool addedToCart;
    
    public:
        // constructor
        Item(std::string name, double price, unsigned int quantity,
            unsigned int amountInCart, bool addedToCart)
            : name{ name } // or name{ std::move(name) } when you learn about the move-semantics
            , price{ price}
            , quantity{ quantity }
            , amountInCart{ amountInCart }
            , addedToCart{ addedToCart }
        {}
        // ... getter and setter functions
    };
    

    You create an object now like:

    Item cake{ "Cake", 3.00, 5, 0, false };
    
  • Please do not practice with using namespace std;. See here for more: Why is "using namespace std;" considered bad practice?
  • I have passed the Machine object to the function test(also in range based loop, Item objects) passed by const qualified referance. Whenver, the data is non-modifiable under the any scopes, you should pass the parameters like that, so that unwanted copying can be avoided(creadits @Klaus): . For further readings: Is it better in C++ to pass by value or pass by constant reference?

哦,我懂了。那很简单。非常感谢您的快速回复和帮助! - cappyt
你应该使用auto&而不是auto!否则,你会在迭代每个字符串时复制一份! - Klaus
@Klaus 哦,当然。已经在答案中添加了。感谢您指出。 - JeJo
也许你可以添加一些提示,为什么要使用ref。你指出了“只读”,但这并没有描述这里需要ref的“原因”。我之所以提到这一点,是因为OP的原始代码与c++相距甚远,因此在这里帮助所有重要的c++特性可能是一个好主意,使你的答案更有帮助。顺便说一句:不需要构造函数,因为你可以使用聚合初始化和=默认值也足够了。好吧,它越来越大了 :-) - Klaus
1
@Klaus 添加更多有用的注释当然是好的,但我想解释一下 OP 在代码中真正遇到的问题。代码改进应该始终是无关的评论,除非它需要在答案中提出。我已经更新了内容,并包含了更多阅读材料。 - JeJo

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