* static *成员函数的const和非const版本

时间:2016-01-04 17:53:45

标签: c++ const

我有两个版本的相同的静态成员函数:一个采用指向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参数 通常应该使用哪种方法?两者都正确吗?

2 个答案:

答案 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的独立函数。