C ++命名参数实现与派生类

时间:2017-09-18 20:55:43

标签: c++ named-parameters

我正在尝试为项目的某些类创建类似于命名参数的构造函数。

我这样做的方法是定义一个类代理,它将保存参数并将此代理的实例传递给我的类的构造函数。

一切正常,直到我必须派出我的一个班级。

基本上我想:我要从基类代理派生新的派生类代理。这也有效,但前提是我只使用派生的代理类参数。

这是一个例子,因为它更容易理解:

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

基本上,因为我在设置参数时链接返回对代理类实例的引用的参数,所以当从派生代理类使用它时它会中断,因为它将返回对基本代理类的引用而不是派生代理类的引用,不允许我访问派生的代理参数,也不将其传递给派生类的构造函数。

任何人都知道如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

此问题最常见的解决方案是奇怪的重复模板模式(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 :(得分:1)

也许协变返回类型正是您所寻找的 有关详细信息,请参阅here

您可以将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上查看并运行。