同时使用const和non-const进行参数传递

时间:2019-05-13 17:59:07

标签: c++ const

假设我有一个对象

class Person {
  std::string name;
  // other important fields
  ... 
}

现在我想将其传递给名为updateName的函数

void updateName(std::string& mutableName, const Person& person)

这是有效功能吗?我会这样传递,对于Person即时p:

updateName(p.name, p)

这对我来说有点奇怪,因为我说我将更改名称,但是第二个参数表明p是const。显然,我只能传递一个可变的Person实例。问题是这是在一个大型系统中,我无法更改现有接口,但可以向其中添加参数。

2 个答案:

答案 0 :(得分:4)

这可能看起来很奇怪,但是很好。 const Person& person表示person中的constupdateName中的p,并不意味着您传递给该函数的person对象现在是const。

如果在调用代码中constp.name,则const std::string将不起作用,因为它的类型为original_folder=$(cygpath -w "/old/path") create_link_new_folder=$(cygpath -w "/link/path") cmd <<< "mklink /j \"${create_link_new_folder}\" \"${original_folder}\"" > /dev/null ,并且您不能将引用绑定到non const指向const对象。


我确实要质疑提供您要修改的对象以及要修改的字段的目的。只需传递字段就足够了,因为您不能修改对象。

答案 1 :(得分:-3)

签名为:

的函数
void updateName(std::string & mutableName, const Person & person);

有效。但是,如果mutableNameperson的一部分,则可能会遇到不确定的行为。考虑以下来自维基百科(https://en.wikipedia.org/wiki/Undefined_behavior)的示例:

i = i++ + 1;  // undefined behavior
a[i] = i++;  // undefined behavior

鉴于它们是未定义的行为,其解释是:

Modifying an object between two sequence points more than once produces undefined behavior.

对于您的updateName函数,您可能会认为其中一个参数在另一个参数之前进行了求值或传递,但这根本不能保证。

几年前,有人告诉我const关键字是人类程序员和编译器之间的约定,该变量不会更改。在updateName()中,我们看到此const协议与第二个参数一起使用,因此我们(程序员)有义务遵守该协议。

请注意,这种const协议有很多偷偷摸摸的方法,您可以找到足够的努力,但是它们几乎总是冒着不确定行为的风险。如您所显示,一种这样的方式是将p.name作为第一个参数,而将p作为第二个参数。

如果您真的想使用类似这样的通话:

updateName(p.name, p);

那么如果您将&令牌从签名中删除,使其看起来像这样,则可能会很好:

void updateName(std::string & mutableName, const Person person);

这样,将传递Person的副本,并且更改mutableName不会干扰person

或者您可以考虑同时使用两个参数const &,然后仅返回新名称,如下所示:

// Will return a newer, better name to use:
std::string updateName(const std::string & name, const Person & person);

现在您可以使用类似的呼叫

p.name = updateName(p.name, p);

不必担心p.name的更改会干扰updateName()的行为。


编辑:

因此,正如评论者所指出的那样,代码毕竟不是未定义的。即使不允许直接在函数本身中更改const引用,也可以通过其他方式进行更改。

我想指出的是,虽然person 可以进行技术更改,但对于维护该代码的维护人员来说并不明显。毕竟,它是 声明为const,因此,某些(或大多数?)知识渊博的维护者将(错误地)假定其值在函数内不会改变。不仅如此,而且当他们不在函数调用中查看函数调用本身时,也不太明显。

我在维护代码时遇到了这样的错误:传入的对象(不一定是const)神秘地修改了属性。我想,“没问题,我将找到修改该属性的代码行并对其进行更正。”我怎么错了...一天后的大部分时间,我花了很多时间逐步调试每行,发现属性不是更改,而是分配给 ,而是当调用完全不同的代码行时。

这会导致奇怪且难以发现的错误。

我注意到您的updateName()函数未返回任何内容。这是返回更新的name的理想场所。

不幸的是,您无法更改现有的界面,因此即使修改了person,看起来可能仍然保持不变,您也可能会受制于某人的旧决定来修改传入的内容。 / p>

如果您确实想传递person而不冒被修改的危险,则可以 传递person的副本,并带有:

updateName(p.name, Person(p));

Person(p)将调用Person复制构造函数,并保留原始的p

这类似于您调用需要const引用的函数时,如下所示:

// Returns double of the input:
int doubler(const int & n)
{
    return n * 2;
}

有人认为您只能传入整数变量,而禁止使用整数文字(例如7-12)。

但是,仍然允许您传递整数文字;编译器足够聪明,可以在此处执行正确的操作。

因此,当您在帖子中说:

Obviously I can only pass in a mutable Person instance.

我不确定在这里我是否同意。尝试传入Person(p)(代替p),看看是否可以编译。然后,您可以调用updateName()函数,而不必担心修改p对象。

(也就是说,如果不修改p确实是您想要的。)

我希望这会有所帮助,对于我答复的第一部分引起的任何困惑,我们深表歉意。