【问题标题】:Does it make sense to modify in-place AND return a copy?就地修改并返回副本是否有意义?
【发布时间】:2012-10-09 22:46:57
【问题描述】:

注意:我标记了这个 Python 和 C++,因为我已经看到了这两个例子,但问题是与语言无关的。

修改对象的函数或类方法有两种选择:直接修改相关对象中的数据,或者创建一个新副本并返回它,同时保持原始数据不变。通常,您可以通过查看函数返回的内容来判断哪个是哪个。

有时您会发现一个函数会尝试同时执行这两种操作,即修改原始对象,然后返回对该对象的副本或引用。有没有一种情况比只做一个或另一个更有优势?

我见过 Fluent InterfaceMethod Chaining 的示例,它依赖于返回对对象的引用,但这似乎是一种特殊情况,在上下文中应该很明显。

我的第一个坏例子直接来自Python documentation,它说明了可变默认参数的问题。对我来说,这个例子是不切实际的:如果函数修改了它的参数,那么有一个默认值是没有意义的,如果它返回一个副本,那么应该在任何修改发生之前制作副本。问题的存在只是因为它试图两者兼而有之。

def f(a, L=[]):
    L.append(a)
    return L

第二个示例来自 Microsoft C++ 中的CStringT::MakeUpper 函数。文档说明了返回值:

返回字符串的副本,但全部为大写字符。

这导致人们期望原件保持不变。部分问题在于文档具有误导性,如果您查看原型,您会发现它返回了对字符串的 reference。除非仔细观察,否则您不会注意到这一点,并且将结果分配给新字符串编译不会出错。惊喜在后面。

【问题讨论】:

  • 除了人们的意见之外,我不确定这个问题是否有答案。我的意见是坚决不,这没有意义。您对 Python 的可变默认参数问题示例的评论是我一直在说的;真正的问题不在于可变的默认参数,而是该参数(仍可在外部使用)被就地修改。
  • @Ben,可以通过一个很好的反例来回答。
  • @delnan,一个函数不应该修改它的参数,除非它被明确记录为这样做,因此默认参数永远不会被修改。如果需要修改,则无论是否使用默认值,都应进行复制。
  • @rici 你需要签出sorted
  • @rici,不,恰恰相反——有一个函数可以就地变异,另一个函数返回修改后的结果。您没有一种功能可以同时兼具这两种功能。

标签: c++ python mutable


【解决方案1】:

C++ 示例 Inc/Dec 运算符

// Pre-Increment: Create a new object for return and modify self.
myiterator  operator++(int) {myiterator tmp(*this); operator++(); return tmp;}


// Post-Increment: modify self and return a reference
myiterator&  operator++() {/* Do Stuff*/ return *this;}

【讨论】:

  • 很好的例子,隐藏在众目睽睽之下。
  • 我认为这不太合适。您要么复制并返回(未修改),要么没有复制并返回参考。
  • @Xeo: Pre: 是 1) 复制。 2)修改自我(就地)。 3)返回副本。
【解决方案2】:

在 C++ 中有一些明显的例子,你想修改对象并返回一个引用:

  1. 作业:

    T & T::operator=(T && rhs)
    {
        ptr = rhs.ptr;
        rhs.ptr = nullptr;
        return *this;
    }
    

    这个修改了对象本身和参数,它返回一个对自身的引用。这样你就可以写a = b = c;了。

  2. IOStreams:

    std::ostream & operator<<(std::ostream & os, T const & t)
    {
        os << t->ptr;
        return os;
    }
    

    同样,这允许链接操作,std::cout &lt;&lt; t1 &lt;&lt; t2 &lt;&lt; t3;,或典型的“提取和检查”if (std::cin &gt;&gt; n) { /* ... */ }

基本上,返回对其中一个输入对象的引用总是用于链式调用或以某种形式评估结果状态,并且有几个有用的场景。

另一方面,修改参数然后返回对象的副本似乎不太有用。

【讨论】:

  • 这些都是很好的例子,但有人可能会争辩说它们都属于“方法链”的范畴,问题排除在特殊情况下。
  • @senderle:嗯...如果您要丢弃返回值,那么讨论返回类型是没有意义的。如果您正在使用返回值,那么这始终是一种“链接”,不是吗?
  • @KerrekSB 如果你能想出一个返回不同对象的好用例,那就不行了。
  • 请注意,a = b = c 变为 a = (b = c),因此返回一个副本即可。对于链接,(a = b) = c 需要引用。
猜你喜欢
  • 2020-03-20
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
  • 1970-01-01
  • 1970-01-01
  • 2019-09-06
  • 2022-06-23
相关资源
最近更新 更多