Non-aggregate initialization

79 Views Asked by At

I need to implement an AutoDocs class that will contain information about the license plate number and driver's full name. It is necessary to implement the class with the following invariants: the correct license plate, consisting of 6 characters (according to pattern x123xx, where x is a letter and 123 is a number) and the correct full name. These classes need to be stored in an array of size N. The problem is that when trying to default initialize and simply when defining an array, it seems to me that the default deleted constructor is called, which should not happen, since autoDocs instances should always be in a stable state and never been default initialized. How can I solve this problem?

class FullName {
public:
    FullName() = delete;

    // class invariant : nor m_name and m_surname must be empty and contain numbers
    FullName(std::string_view name, std::string_view surname)
        : m_name{ (assert(isvalid(name) && "INVALID NAME"), name) }
        , m_surname{ (assert(isvalid(surname) && "INVALID SURNAME"), surname) } 
    {}

    auto setName(std::string_view name) { if (isvalid(name)) m_name = name; }
    auto setSurname(std::string_view surname) { if (isvalid(surname)) m_surname = surname; }

    bool isvalid(std::string_view str) const;

private:
    std::string m_name{};
    std::string m_surname{};
};

bool
FullName::isvalid(std::string_view str) const {
    if (str.empty()) return false;
    if (std::any_of(std::begin(str), std::end(str), [=](char ch) -> bool { return isdigit(ch); })) return false;
    return true;
}



class StateNumber {
public:
    StateNumber() = delete;

    // class invariant : string length == 6 && matches the number template
    StateNumber(std::string_view stateNumber)
        : m_letters{ ( assert(isvalid(stateNumber) && "INVALID STATE_NUMBER"), (std::string(1, stateNumber[0]) + stateNumber[4] + stateNumber[5]) ) }
        , m_numbers{ static_cast<std::uint16_t>(std::stoi(std::string(1, stateNumber[1]) + stateNumber[2] + stateNumber[3])) }
    {}

    bool isvalid(std::string_view s) const { return s.length() == 6 && isalpha(s[0]) && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3]) && isalpha(s[4]) && isalpha(s[5]); }

    auto setNumbers(std::uint16_t numbers) { if (numbers <= 999) m_numbers = numbers; }
    auto setLetters(std::string_view letters);

private:
    std::string m_letters{};
    std::uint16_t m_numbers{};
};

auto
StateNumber::setLetters(std::string_view letters) {
    if (std::any_of(std::begin(letters), std::end(letters), [=](char ch) -> bool { return isdigit(ch); })) return;
    if (letters.length() != 3) return;

    std::string letters_copy{ letters };
    std::transform(std::begin(letters_copy), std::end(letters_copy), std::begin(letters_copy), [&](char ch){ return std::tolower(ch); } );
    m_letters = letters_copy;
}


class AutoDocs {
public:
    AutoDocs() = delete;

    AutoDocs(const FullName& fullName, const StateNumber& stateNumber) :    
        m_fullName{ fullName }, m_stateNumber{ stateNumber }
    {}

private:
    FullName m_fullName{};
    StateNumber m_stateNumber{};
};


int
main() {
    std::uint32_t N{};
        AutoDocs array[N]; // and AutoDocs array[N] doesn't work in expected way
    return 0;
}   

I tried to explicitly add default constructor and, happily, it worked. But it doesn't solve the problem of compliance with the invariants.

1

There are 1 best solutions below

0
On BEST ANSWER

As noted in the comments, use std::vector<AutoDocs>. Unlike raw arrays, it supports classes which don't have default constructors.

#include <vector>

...

int
main() {
    std::vector<AutoDocs> array; // initially, the list is empty

    for (int i = 0; i < 10; ++i)
    {
        // Read information from somewhere
        std::string name, surname, number;
        std::cin >> name >> surname >> number;

        // Construct an object and add it to the list of objects
        array.emplace_back(FullName{name, surname}, StateNumber{number});
    }
    return 0;
}