只写对象的链接操作符= - OK返回rhs而不是*this

Chaining operator= for write-only objects - OK to return rhs instead of *this?

本文关键字:rhs 返回 this OK 对象 链接 操作符      更新时间:2023-10-16

众所周知,operator=应该返回一个const*this的引用来支持链接,但这只适用于*this 可以用作右值与值类似的情况。

编辑:好的,operator=应该返回一个非const引用(做int s),我的意思是*this需要在赋值中成为一个有意义的rhs

我包装一个C API的名称=值设置函数通过一个c++类ApiWrapperoperator[]返回一个临时只写Proxy与重载operator=,但API没有getter函数,所以Proxy是有效的只写。

ApiWrapper x;
x["a"] = x["b"] = 42;  // x["b"] = 42;      fine:  consumes 42, returns *this
                       // x["a"] = x["b"];  error: x["b"] does not have the value

在我看来,如果我从operator=返回一个const引用到rhs而不是*this,链接将工作得很好。概念上(省略代理样板代码):

struct Proxy {
    template <typename T>
    T const& operator=(T const& rhs) const
    {
        ...         // pass rhs to the API but don't store it
        return rhs; // return rhs, not *this
    }
};
ApiWrapper x;
x["a"] = x["b"] = 42;  // x["b"] = 42;   fine: consumes and returns 42
                       // x["a"] = 42;   fine: consumes and returns 42

这让我怀疑。是否有任何奇怪的副作用从返回一个const引用rhs而不是*this ?我唯一能想到的是,我将无法在像(x["a"] = 42).doSomething()这样的表达式中使用它,但我的Proxy无论如何都不能支持这样的东西,因为它是只写的。或者干脆不允许链接(例如通过返回void)会更好吗?

Edit:即使Proxy不是类值的,我认为支持赋值是有意义的,它允许语法糖,如:

// this:                          // rather than:
ApiWrapper w;                     API * ptr = make_api_instance();
w["name"] = "Batman";             api_set_str(ptr, "name", "Batman");
w["age"]  = 42;                   api_set_int(ptr, "age", 42);
w["pi"]   = 3.14;                 api_set_double(ptr, "pi", 3.14);

我认为最干净的解决方案是坚持使用标准习惯用法。如果您以通常的方式使代理类具有可复制构造性和可复制赋值性,这应该可以工作。像这样:

struct Proxy
{
    Proxy(Proxy const & rhs)
    : // ...
    {
        // copy internal state of rhs
    }
    Proxy & operator=(Proxy const & rhs)
    {
        // copy internal state of rhs
        return *this;
    }
    template <typename T>
    Proxy & operator=(T const & rhs)
    {
        // ... perform T-specific operations ... #1
        return *this;
    }
};

一个额外的好处是,无论在#1的第一个赋值中必须执行什么"泛型逻辑",都不需要在每个后续赋值中重复。

我认为你的方法很有道理。只是为了检查我是否正确理解了你的问题,结构可以看起来像这样:

struct Proxy {
    template <typename T>
    T const& operator=(T const& rhs) const
    {
        send_to_abi(rhs);
        return rhs;
    }
};

正如你所说,由于代理不存储rhs在任何地方,我假设没有receive_from_abi函数存在,那么返回*this将不起作用-在这种情况下,数字不会传播。正如评论所指出的,一些行为,如(a=3)=3将不起作用,但这并不奇怪。

编辑:正如评论所指出的,如果rhs是临时的,这种方法是危险的。这可以通过返回一个副本来修复:

struct Proxy {
    template <typename T>
    T operator=(T const& rhs) const
    {
        send_to_abi(rhs);
        return rhs;
    }
};

这可能看起来很昂贵,a["a"] = a["b"] = a["c"] = foo看起来像是涉及3个副本。但是这些应该可以通过一个通用的编译器优化来避免。