简体   繁体   中英

const and non-const versions of *static* member functions

I have two versions of the same static member function: one takes a pointer-to-const parameter and that takes a pointer-to-non-const parameter. I want to avoid code duplication.
After reading some stack overflow questions (these were all about non-static member functions though) I came up with this:

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
};

(I know juggling with pointers is generally a bad idea, but I'm implementing a data structure.)

I found some code in libstdc++ that looks like this:
NOTE: these are not member functions

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));
}

In the first approach the code is in a function that takes a pointer-to-const parameter.
In the second approach the code is in a function that takes a pointer-to-non-const parameter.
Which approach should generally be used? Are both correct?

The most important rule is that an interface function (public method, a free function other than one in a detail namespace, etc), should not cast away the constness of its input. Scott Meyer was one of the first to talk about preventing duplication using const_cast, here's a typical example ( 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;

};

This refers to instance methods rather than static/free functions, but the principle is the same. You notice that the non-const version adds const to call the other method (for an instance method, the this pointer is the input). It then casts away constness at the end; this is safe because it knows the original input was not const.

Implementing this the other way around would be extremely dangerous. If you cast away constness of a function parameter you receive, you are taking a big risk in UB if the object passed to you is actually const. Namely, if you call any methods that actually mutate the object (which is very easy to do by accident now that you've cast away constness), you can easily get UB:

C++ standard, section § 5.2.11/7 [const cast]

[ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a const_cast that casts away a const-qualifier may produce undefined behavior. —end note ]

It's not as bad in private methods/implementation functions because perhaps you carefully control how/when its called, but why do it this way? It's more dangerous to no benefit.

Conceptually, it's often the case that when you have a const and non-const version of the same function, you are just passing along internal references of the object ( vector::operator[] is a canonical example), and not actually mutating anything, which means that it will be safe either way you write it. But it's still more dangerous to cast away the constness of the input; although you might be unlikely to mess it up yourself, imagine a team setting where you write it the wrong way around and it works fine, and then someone changes the implementation to mutate something, giving you UB.

In summary, in many cases it may not make a practical difference, but there is a correct way to do it that's strictly better than the alternative: add constness to the input , and remove constness from the output .

I have actually only ever seen your first version before, so from my experience it is the more common idiom.

The first version seems correct to me while the second version can result in undefined behavior if (A) you pass an actual const object to the function and (B) the long code writes to that object. Given that in the first case the compiler will tell you if you're trying to write to the object I would never recommend option 2 as it is. You could consider a standalone function that takes/returns const however.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM