假设我有一个对象
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实例。问题是这是在一个大型系统中,我无法更改现有接口,但可以向其中添加参数。
答案 0 :(得分:4)
这可能看起来很奇怪,但是很好。 const Person& person
表示person
中的const
是updateName
中的p
,并不意味着您传递给该函数的person对象现在是const。
如果在调用代码中const
是p.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);
有效。但是,如果mutableName
是person
的一部分,则可能会遇到不确定的行为。考虑以下来自维基百科(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
确实是您想要的。)
我希望这会有所帮助,对于我答复的第一部分引起的任何困惑,我们深表歉意。