0
#include <iostream>
#include <array>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <random>
#include <iterator>
#include <string>

class Deck;

class Card
{
public:
    enum cRanks
    {
        RANK_2,
        RANK_3,
        RANK_4,
        RANK_5,
        RANK_6,
        RANK_7,
        RANK_8,
        RANK_9,
        RANK_10,
        RANK_J,
        RANK_Q,
        RANK_K,
        RANK_A,

        MAX_RANKS,
        RANK_AIR
    };

    enum cSuits
    {
        SUIT_CLUBS,
        SUIT_DIAMONDS,
        SUIT_HEARTS,
        SUIT_SPADES,

        MAX_SUITS,
        SUIT_AIR
    };

private:
    cRanks m_rank{RANK_A};
    cSuits m_suit{SUIT_SPADES};

public:
    //Card() = default;

    Card(cRanks r, cSuits s)
        :m_rank{ r }, m_suit{ s }
    {
    }

    void print() const
    {
        char rank{};
        char suit{};
        switch (m_rank)
        {
            case(RANK_2): rank = '2'; break;
            case(RANK_3): rank = '3'; break;
            case(RANK_4): rank = '4'; break;
            case(RANK_5): rank = '5'; break;
            case(RANK_6): rank = '6'; break;
            case(RANK_7): rank = '7'; break;
            case(RANK_8): rank = '8'; break;
            case(RANK_9): rank = '9'; break;
            case(RANK_10): rank = 't'; break;
            case(RANK_J): rank = 'J'; break;
            case(RANK_Q): rank = 'Q'; break;
            case(RANK_K): rank = 'K'; break;
            case(RANK_A): rank = 'A'; break;
            case(RANK_AIR): rank = '_'; break;
            default: rank = '?';
        }
        switch (m_suit)
        {
            case(SUIT_CLUBS): suit = 'C'; break;
            case(SUIT_DIAMONDS): suit = 'D'; break;
            case(SUIT_HEARTS): suit = 'H'; break;
            case(SUIT_SPADES): suit = 'S'; break;
            case(SUIT_AIR): suit = '_'; break;
            default: suit = '?';
        }
        std::cout << rank << suit << ' ';
    }

    friend Deck; 
};

class Deck
{
public:
    using array_type = std::array<Card, 52>;
    using index_type = int;
private:
    array_type m_deck;
    index_type m_index{ 0 };

public:
    Deck()
    {
        m_index = 0;
        int index{0};
        for (int s{ 0 }; s < Card::MAX_SUITS; ++s)
        {
            for (int r{ 0 }; r < Card::MAX_RANKS; ++r)
            {
                m_deck[index].m_rank = static_cast<Card::cRanks>(r);
                m_deck[index].m_suit = static_cast<Card::cSuits>(s);
                index++;
            }
        }
    }

    void print()
    {
        if (m_index > static_cast<int>(m_deck.size()) - 1)
        {
            std::cout << "\nDeck out of cards!";
            return;
        }
        std::cout << '\n';
        for (int i{ m_index }; i < m_deck.size(); ++i)
            m_deck[i].print();
    }

    void shuffle(unsigned int seed = 777)
    {
        std::mt19937 g{ seed };
        std::shuffle(m_deck.begin(), m_deck.end(), g);
    }

    const Card& draw()
    {
        if (m_index > static_cast<int>(m_deck.size()) - 1)
        {
            std::cout << "\nDeck out of cards!";
            return Card(Card::RANK_AIR, Card::SUIT_AIR);
        }
        return m_deck[m_index++];
    }
};

int main()
{
    Deck deck{};
    deck.print();
    deck.shuffle();
    deck.print();



    return 0;
}

Simple BlackJack game-exercise to learn OOP. In the above code I get the error message "the default constructor of cannot be referenced -- it is a deleted function" on my Deck constructor. Is it wise to assume that my m_deck array cannot be instantiated so that the constructor of Deck can assign values to it because the Card class has no default constructor? What if I want my default Card constructor to have arguments? Can I avoid using "Cards() = default;" in my Cards class? I was thinking that it is only sensible to instantiate a Card object only if you define it's nature (rank and suit).

Windfish
  • 68
  • 7
  • 2
    [This question](https://stackoverflow.com/questions/18295302/default-initialization-of-stdarray) might give you an answer, though I'm not sure it answers your specific case as clearly as I'd like. If you have questions after reading that, perhaps edit your question. –  May 02 '20 at 18:59
  • 1
    You need the default constructor to initialize the elements of the array or you have to provide the necessary arguments with [list initialization](https://en.cppreference.com/w/cpp/language/list_initialization)(which replaces the need for the nested `for` loops). There are a other alternatives, but [they get weird.](https://en.cppreference.com/w/cpp/language/new#Placement_new) – user4581301 May 02 '20 at 19:13

1 Answers1

0
  1. There is no recipe to construct an object of Card() from the returning type const Card& in the absence of the copy constructor; constexpr Card::Card(const Card&)
  2. The compiler would like to use default constructor provided by you or generated by it to match the return type Card in std::array. Hence, /usr/include/c++/7/array:94:12: error: no matching function for call to ‘Card::Card()
  3. I tried to replicate your code with bare minimum things with addition of Card() = default; Card(const Card& other) = default; It seems to be compiling at least. I can't check functional aspects for you.
#include <iostream>
#include <array>

class Deck;

class Card
{
public:
    enum cRanks
    {
        RANK_A,
        MAX_RANKS,
        RANK_AIR
    };

    enum cSuits
    {
        SUIT_SPADES,
        MAX_SUITS,
        SUIT_AIR
    };
private:
    cRanks m_rank{RANK_A};
    cSuits m_suit{SUIT_SPADES};

public:
    Card() = default;

    Card(const Card& other) = default;

    Card(cRanks r, cSuits s)
        :m_rank{ r }, m_suit{ s }
    {
    }

    void print() const
    {
        char rank{};
        char suit{};
        switch (m_rank)
        {
            case(RANK_A): rank = '_'; break;
            default: rank = '?';
        }
        switch (m_suit)
        {
            case(SUIT_SPADES): suit = 'C'; break;
            default: suit = '?';
        }
        std::cout << rank << suit << ' ';
    }

    friend Deck; 
};

class Deck
{
public:
    using array_type = std::array<Card, 52>;
    using index_type = int;
private:
    array_type m_deck;
    index_type m_index{ 0 };

public:
    Deck()
    {
    }

    void print()
    {
    }

    void shuffle(unsigned int seed = 777)
    {
    }

    const Card& draw()
    {
    }
};

int main()
{
    Deck deck{};
    deck.print();
    deck.shuffle();
    deck.print();



    return 0;
}
Sowmya
  • 91
  • 1
  • 9
  • The first point is incorrect. While I generally recommend `Card(const Card& other) = default;` as you've done in the code example to be brutally explicit, noting here is preventing the compiler from auto-generating a defaulted copy constructor without prompting. What `constexpr` adds to the operation is not clear. You should explain that a bit more. – user4581301 May 02 '20 at 20:00
  • In the second point, the wording is confusing. `Card` is not a return type, and this makes the explanation of what went wrong hard to follow. – user4581301 May 02 '20 at 20:03
  • the third point deflects the question. The questioner was wondering if there was a way, other than `Card() = default;`, to make their code work. You do not address this. – user4581301 May 02 '20 at 20:04
  • You suggested in your answer that the user of this code either need the default constructor to initialize the elements of the array or some other way to get around. I think the user of this class has to decide between your answer and their choice of not providing a default constructor, even though I would like the rationale behind it. – Sowmya May 02 '20 at 20:08