0

To better explain, I have the following constructor in class Card:

Card(std::string& s);

Therefore it only needs one string to create a Card object. However in another file where I have another class called Deck, which is derived from class Card, this class roughly looks like this:

deck.h:

class Deck : public Card
{
  Card deck[52]; //this will hold 52 cards

};

deck.cpp:

Deck::Deck()
{
  std::string value[13] = {"2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"};
  std::string suit[4] = {"H", "D", "C", "S"};
  int k = 0;
  for(int i = 0; i < 4; i++){
    for(int j = 0; j < 13; j++){
      //std::string str = value[j] + suit[i];
      //deck[k] = str; //CREATES ALL 52 CARDS //<---INSTEAD OF DOING THIS
      deck[k] = value[j] + suit[i]; //<---WHY CAN'T I SIMPLY DO THIS
      k++;
    }
  }
}

Its just a simple question I am just curious as to why its so pedantic, because a sum of two strings is one string, so it should work.

Here is the error:

error: no match for ‘operator=’ (operand types are ‘Card’ and ‘std::basic_string<char>’)
       deck[k] = value[j] + suit[i];
  • 1
    A `Card` is not a `std::string`. Also, the error shows this as an assignment going on, `error: no match for ‘operator=’ ` not a construction, so the constructor does not come into play. – PaulMcKenzie Dec 03 '15 at 16:14
  • 5
    Why is `Deck` a derived class of `Card`? Doesn't make much sense – McLovin Dec 03 '15 at 16:16
  • 4
    Your inheritance for `Deck` just seems wrong to me. A deck is not a card. – Some programmer dude Dec 03 '15 at 16:16
  • Well I have just started writing programs with classes, so I am not very good, my idea is to create a deck of 52 card objects, therefore to create a deck I need to tell the program what a "card" is. –  Dec 03 '15 at 16:18
  • You've already constructed your deck array. Your class has no provisions on assigning a string to an already constructed `Card`. Yes, you can *create* a `Card` from a string, but your current class lacks any means of taking an *existing* `Card` and give it a string. – PaulMcKenzie Dec 03 '15 at 16:20
  • So I should make an array of 52 strings and then construct cards from those? I apologize for being slow. –  Dec 03 '15 at 16:27

2 Answers2

2

Because your constructor takes a non-const reference. Non-const references cannot be bound to temporary values. You should make your constructor accept a const reference.

Card(const std::string& s)
Community
  • 1
  • 1
Fred Larson
  • 60,987
  • 18
  • 112
  • 174
  • That fixed it, thank you, although I still am not sure as to why it must be const –  Dec 03 '15 at 16:22
0

First of all prefer std::array<> over legacy C-Arrays:

class Deck : public Card {
  std::array<Card, 52> deck; //this will hold 52 cards
};

Now to your question:

Card(std::string& s);

here s is an lvalue-reference to a string.

deck[k] = value[j] + suit[i]

C++ will generate a temporary string which holds the value value[j] + suit[i]. That can not be a lvalue-reference, because a lvalue-reference parameter is expected to be used after the call. The temporary can not be used, because it is a temporary. Therefore the standard just does not let you do that.

In case the lvalue-reference is const, it is obvious, that the referenced object can not be changed inside the function. Therefore the C++ standard allows you to pass a temporary as well.

So the solution is to use

Card(const std::string& s);

With C++11 you can also use the rvalue-reference to move the string into your new object instead of copying it:

Card(const std::string&& s) :
  name_{std::move(s)} {};

Here the temporary string will be passed by reference. The move cosntruction of the member variable name_ will simply "steal" the memory that contains the string from the passed parameter, so that the string is never copied.

cdonat
  • 2,748
  • 16
  • 24
  • That is nicely explained thank you, I had to look up what is exactly meant by an lvalue, I think I get it now. –  Dec 03 '15 at 16:35