C++ named arguments implementation with derived classes

吃可爱长大的小学妹 提交于 2019-12-05 19:45:15

Maybe covariant return types are what you are looking for.
See here for more details.


You can define PersonArgs as (note virtual keywords placed around):

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;
};

Then define PersonExArgs as (note override and the covariant return type):

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;
};

Probably it's annoying for you have to override each and every function in the base class but it does the work nicely.
See it up and running on wandbox.

The most common solution to this problem is the 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;
};

In your case, you could combine it with another base class to clean up the interface:

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>
    {
        ...
    };
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!