我有两个版本的相同的静态成员函数:一个采用指向const的指针参数,并采用指向非const的参数。我想避免代码重复。
在阅读了一些堆栈溢出问题(这些都是关于非静态成员函数)之后我想出了这个:
class C {
private:
static const type* func(const type* x) {
//long code
}
static type* func(type* x) {
return const_cast<type*>(func(static_cast<const type*>(x)));
}
public:
//some code that uses these functions
};
(我知道处理指针通常是一个坏主意,但我正在实现一个数据结构。)
我在libstdc ++中发现了一些看起来像这样的代码:
注意:这些不是成员函数
static type* local_func(type* x)
{
//long code
}
type* func(type* x)
{
return local_func(x);
}
const type* func(const type* x)
{
return local_func(const_cast<type*>(x));
}
在第一种方法中,代码位于一个带指针到const参数的函数中 在第二种方法中,代码在一个函数中,该函数采用指针到非const参数 通常应该使用哪种方法?两者都正确吗?
答案 0 :(得分:1)
最重要的规则是接口函数(公共方法,详细命名空间中的一个以外的自由函数等)不应抛弃其输入的常量。 Scott Meyer是第一个谈论使用const_cast防止重复的人之一,这是一个典型的例子(How do I remove code duplication between similar const and non-const member functions?):
struct C {
const char & get() const {
return c;
}
char & get() {
return const_cast<char &>(static_cast<const C &>(*this).get());
}
char c;
};
这是指实例方法而不是静态/自由函数,但原理是相同的。您注意到非const版本添加了const来调用另一个方法(对于实例方法,this
指针是输入)。然后它最后抛弃了constness;这是安全的,因为它知道原始输入不是常量。
反过来实现这一点非常危险。如果你强制转换你收到的函数参数的 constness,如果传递给你的对象实际上是const,你在UB中冒的风险很大。也就是说,如果你调用任何实际改变对象的方法(现在你已经抛弃const就很容易做到),你可以轻松获得UB:
C ++标准,第5.2.11 / 7节[const cast]
[注意:根据对象的类型,通过指针,左值或指向数据成员的指针进行写操作 抛弃const限定符的const_cast可能会产生undefined 行为。 - 后注]
在私有方法/实现函数中并没有那么糟糕,因为你可能会仔细控制它被调用的方式/时间,但为什么这样做?没有任何好处会更危险。
从概念上讲,通常情况下,当你有一个相同函数的const和非const版本时,你只是传递对象的内部引用(vector::operator[]
是一个规范的例子),而不是实际的改变任何东西,这意味着你写它的方式都是安全的。但抛弃输入的常量仍然更危险;虽然你可能不太可能弄乱自己,想象一个团队设置,你用错误的方式编写它并且工作正常,然后有人改变实现以改变某些东西,给你UB。
总之,在许多情况下,它可能没有实际的区别,但是有一种正确的方法可以做到明显优于替代方案:将 constness添加到输入,从输出中删除 constness。。
答案 1 :(得分:0)
我以前只见过你的第一个版本,所以根据我的经验,这是更常见的习语。
第一个版本对我来说似乎是正确的,而第二个版本可能导致未定义的行为,如果(A)您将实际的const对象传递给该函数并且(B)if personList is None:
print "No values in personList"
else:
for person in personList:
# do stuff with person
写入该对象。鉴于在第一种情况下,编译器将告诉您,如果您尝试写入对象,我将永远不会推荐选项2。你可以考虑使用/返回const的独立函数。