Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For each loop only takes first string of array C++

I'm using C++14 and trying to create a for each loop that prints out each string of the array. I get the error:

user.cpp:12:34: error: invalid initialization of reference of type ‘std::string& {aka std::basic_string&}’ from expression of type ‘char’

for(std::string &str : *(u->favs)){

When I change the std::string to auto in the foreach loop, it works but str becomes individual characters of the first string in the favs array. My code is as follows:

user.h

class User{
    private:

    public:
        User(){
            favs = new std::string[5]{std::string("Hello"), std::string("how"), std::string("are"), std::string("you"), std::string("?")};
        }

        ~User(){
            delete[] favs;
        }

        std::string lName;
        std::string fName;
        int age;
        std::string *favs;
};

user.cpp

#include <iostream>
#include "user.h"

void create(User *u){   
    std::cout << "Please enter first name: ";
    std::cin >> u->fName;
    std::cout << "Please enter last name: ";
    std::cin >> u->lName;
    std::cout << "Please enter age: ";
    std::cin >> u->age;

    for(std::string &str : *(u->favs)){
        std::cout << str << std::endl;
    }

    std::cout << "\n";
}

main.cpp

#include <iostream>
#include <string>
#include "user.h"       

int main(){
    std::string command;
    User user;
    std::cout << "bp1: " << user.favs->size() << std::endl;
    while(true){
        std::cout << "Please enter a command (Create, Update, View, Favorites, or Quit): ";
        std::cin >> command;
        if(command == "Create"){
            create(&user);
        } else if(command == "Update"){
            update(&user);
        } else if(command == "View"){
            view(&user);
        } else if(command == "Favorites"){
            //favorites(&user);
        } else if(command == "Quit"){
            break;
        } else std::cout << "Please input a valid command.\n";
    }
}
like image 401
a0935 Avatar asked Feb 13 '16 01:02

a0935


2 Answers

When you dereference u->favs you get a std::string&, and range based for then starts iterating over that string one character at a time. This is why when you auto you see individual characters from the first string, and when you try to bind that character to a string& you get a compilation error.

You cannot get a range based for to work with a pointer type, because it has no way of knowing how many objects your pointer points to. If you make the following changes to your code to declare favs as a statically sized array, then your code will work.

Change your class definition to

using namespace std::string_literals;  // allows use of operator""s (user defined literal)
class User
{
public:
    User()
    : favs{{"Hello"s, "how"s, "are"s, "you"s, "?"s}}  // use operator""s
    {}

    // no need for a destructor definition

    std::string lName;
    std::string fName;
    int age;
    std::array<std::string, 5> favs;  // statically sized array
};

If you need a dynamically sized array, then use std::vector<std::string> instead, and your loop will still work.

like image 193
Praetorian Avatar answered Oct 15 '22 04:10

Praetorian


If you want to use the range-based loop, there's 2 ways:

1) std::string favs[n];

range-based loop works against arrays with constant size

2) std::vector<std::string> favs

range-based loop works against objects that have implemented begin / end, and the STL containers do that.

class User{
    public:
        User() : favs {"Hello", "how", "are", "you", "?"} { }
        std::vector<std::string> favs;
};

for(std::string& str : u->favs)
    std::cout << str << std::endl;

3) If favs will be the main array of your class... and you want to try the begin / end idea, here's how you would do it:

class User
{
public:
    std::string *favs;
    int favs_size;
    User()
    {
        favs_size = 5;
        favs = new string[favs_size];
    }
    ~User() { delete[] favs; } 

public:
    std::string* begin() { return favs; }
    std::string* end() { return favs+favs_size ; }
};

User u;
for(std::string &str : u)
    std::cout << str << std::endl;
like image 32
Jts Avatar answered Oct 15 '22 06:10

Jts