C ++ named arguments with derived classes

I am trying to create a constructor with similar arguments for some project classes.

The way I do this is to define the proxy class of the class that will contain the arguments and pass an instance of this proxy file to the constructor of my classes.

Everything worked fine until I had to output one of my classes.

Basically, I thought: I'm going to get a new proxy of a derived class from a proxy of a base class. This also works, but only if I use only derived proxy class arguments.

Here is an example because it is easier to understand:

class Person
{
public:
    class PersonArgs
    {
    public:
        const std::string& Name() const { return _name; }
        PersonArgs& Name(const std::string& name)
        {
            _name = name;
            return *this;
        }

        const std::string& Surname() const { return _surname; }
        PersonArgs& Surname(const std::string& surname)
        {
            _surname = surname;
            return *this;
        }

    protected:
        std::string _name;
        std::string _surname;
    }

public:
    Person()
        : _name("")
        , _surname("")
    { }

    Person(const PersonArgs& args)
        : _name(args.Name())
        , _surname(args.Surname())
    { }

protected:
    std::string _name;
    std::string _surname;
}

class PersonEx : public Person
{
public:
    class PersonExArgs : public Person::PersonArgs
    {
    public:
        const std::string& Address() const { return _address; }
        PersonExArgs& Address(const std::string& address)
        {
            _address = address;
            return *this;
        }

    protected:
        std::string _address;
    }

public:
    PersonEx()
        : _address("")
    { }

    PersonEx(const PersonExArgs& args)
        : Person(args)
        , _address(args.Address())
    { }

protected:
    std::string _address;
}

int main(int argc, char** argv)
{
    // This is ok since PersonExArgs::Address returns a PersonExArgs&
    PersonEx* p1 = new PersonEx(PersonEx::PersonExArgs().Address("example"));

    // This won't work since PersonExArgs::Name returns a PersonArgs&
    PersonEx* p2 = new PersonEx(PersonEx::PersonExArgs().Address("example").Name("Mark"));
}

, , , , , , , .

- , ?

+4
2

Curiously Recurring Template Pattern (CRTP):

template <typename Derived>
class PersonArgs
{
public:
    const std::string& Name() const { return _name; }
    Derived& Name(const std::string& name)
    {
        _name = name;
        return static_cast<Derived&>(*this);
    }

    const std::string& Surname() const { return _surname; }
    Derived& Surname(const std::string& surname)
    {
        _surname = surname;
        return static_cast<Derived&>(*this);
    }

protected:
    std::string _name;
    std::string _surname;
};

...

class PersonExArgs : public Person::PersonArgs<PersonExArgs>
{
public:
    const std::string& Address() const { return _address; }
    PersonExArgs& Address(const std::string& address)
    {
        _address = address;
        return *this;
    }

protected:
    std::string _address;
};

:

class Person {
    class PersonArgsBase
    {
    public:
        const std::string& Name() const { return _name; }
        const std::string& Surname() const { return _surname; }

    protected:
        std::string _name;
        std::string _surname;
    };

    template <typename Derived>
    class PersonArgs : public PersonArgsBase
    {
        Derived& Name(const std::string& name)
        {
            _name = name;
            return static_cast<Derived&>(*this);
        }

        Derived& Surname(const std::string& surname)
        {
            _surname = surname;
            return static_cast<Derived&>(*this);
        }
    };

    ...
};

class PersonEx {
    class PersonExArgs : public Person::PersonArgs<PersonExArgs>
    {
        ...
    };
};
+1

, - , .
. .


PersonArgs ( < virtual):

class PersonArgs
{
public:
    const std::string& Name() const { return _name; }
    virtual PersonArgs& Name(const std::string& name)
    {
        _name = name;
        return *this;
    }

    const std::string& Surname() const { return _surname; }
    virtual PersonArgs& Surname(const std::string& surname)
    {
        _surname = surname;
        return *this;
    }

protected:
    std::string _name;
    std::string _surname;
};

PersonExArgs ( override ):

class PersonExArgs : public Person::PersonArgs
{
public:
    const std::string& Address() const { return _address; }
    PersonExArgs& Address(const std::string& address)
    {
        _address = address;
        return *this;
    }

    PersonExArgs& Name(const std::string& name) override
    {
       PersonArgs::Name(name);
        return *this;
    }

    PersonExArgs& Surname(const std::string& surname) override
    {
        PersonArgs::Surname(surname);
        return *this;
    }

protected:
    std::string _address;
};

, , , .
wandbox.

+1

Source: https://habr.com/ru/post/1685911/


All Articles