6

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";
    }
}
1
  • Additionally, the size of user.favs is the size of the first string of the array, not the number of elements in the array Commented Feb 13, 2016 at 1:07

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

If you want to quickly try vector instead of a basic array, @Praetorian's answer also works through vector's initializer list if you directly swap out std::string favs[5]; from his answer for std::vector<std::string> favs;
Quite correct, but I hesitate to upvote an answer promoting C-arrays, especially in conjunction with other modern C++ constructs.
@ildjarn But std::array is so much more typing compared to a plain C array :)
1

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;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.