开发者

What's wrong with const?

开发者 https://www.devze.com 2023-02-04 01:07 出处:网络
What are the known shortfalls of const in C++ and C+开发者_如何学Go+0x?The only thing wrong with const is that it is seriously underrated by to many developers. It\'s one of the best tools in C++\'s t

What are the known shortfalls of const in C++ and C+开发者_如何学Go+0x?


The only thing wrong with const is that it is seriously underrated by to many developers. It's one of the best tools in C++'s toolbox, very sharp, and yet not dangerous to cut yourself with.


The main problem is that you have to write it. It should be the default, and all mutable variables or parameters should be specified explicitly.


What's wrong with const is that many programmers don't seem to be able to understand it completely, and a "half-const-correct" project simply does not work. This is what you need to know:

  1. Foo vs. const Foo (or Foo const)
  2. Foo& vs. const Foo& (or Foo const&)
    • references-to-const bind to all kinds of things, while references-to-non-const don't
  3. Foo* vs. const Foo* (or Foo const*)
    • pointer variables can also be Foo* const and const Foo* const (or Foo const* const)
  4. void Foo::mutator() vs. int Foo::accessor() const
    • but pointer members inside const member functions still point to non-const objects
    • so we can accidentally return non-const data from a const-function
  5. iterator vs. const_iterator
    • iterator variables can also be const iterator and const const_iterator

Migrating to C++ from a language that has no const concept is quite hard, any many fail to see the point.


The two main issues that I have seen frequent complaints about in newsgroups, are

  • The need to waste a lot of time on supporting non-const-aware APIs (especially Microsoft's).

  • The need to define both const and non-const version of a method.

I think the latter could/should be supported by the language.

Possibly in conjunction with support for covariant member function implementations, because both need some way to pick up the type of the this pointer.

A third issue is that

  • const does not propagate to owned objects.

Cheers & hth.,


The problem with const is the programmers that use it incorrectly our inconsistently


One thing that is "wrong" is that you cannot convert T** to T const * const * which should be allowed because it is not dangerous. Not allowing T** to convert to T const ** is correct, that conversion is not valid.

I have sometimes mentioned that const actually is a cheap way to "split" your interface into read-only methods and write methods. It would probably be impractical in C++. It would be more practical in Java to have ReadOnly versions of collections though, where they don't have const and where their collection types are more object-orientated.

const-ness does not propagate: The issue here is that if I pImpl my class, the constness is not "checked" by the compiler, i.e. my interface class can have a "const" method call a non-const method on the pImpl and the compiler won't complain. That is because the only thing my const method is guaranteed not to do is change the pointer to point to a different object, and my pImpl is never going to change. It could even be a const pointer (not pointer to const).

The lack of proper co-variance between shared_ptr<T> and shared_ptr<const T> might be an issue too, although in general I have seen that not to be the problem, but that developers usually typedef their shared_ptrs and rarely typedef the shared_ptr to const. And they will sometimes pass const shared_ptr<T> & and think that they are passing a shared pointer to a const T (as with const T*) which they are not.


"issues"?

If you aren't going to be modifying the value of a passed pointer (it is used purely for pass-by-reference input to a function), mark it is const. Ditto if the value of a particular variable will not change after its initialization. If a function is safe to call on a const class instance, mark it const too. The more items are correctly annotated const, the less likely you are to inadvertently make a mistake and the more optimizations the compiler will be theoretically able to perform in the absence of complete knowledge (such as when compiling with only function prototypes available).

Modern versions of gcc have support for warning when you try to cast a const variable to a non-const one. I suggest you leave those warnings enabled.

The only thing to watch out for is exactly what you are marking const; const char * foo() is not the same as char * foo() const.


Most of the answers below state things such as "what is wrong with const is that X people do Y". Those are not answers but symptoms. Those are not things wrong with const. There's barely any wrong thing with const... Those are things wrong with people who can't RTFM.


const is great. const is important. const-correctness is necessary condition for an API to be good.

Yet there are two issue I've had with const, repeatedly.

  • There's no way to mark a variable as const retroactively. You must either declare a variable code, in which case you have to initialize it immediatly. What if the initialization code contains an if, though? You have the choice of either omitting the const (undesirably), using operator ? instead of if (harms readability). Java get's that right, BTW - const variables don't have to be initialized right away, they just have to be initialized before they're first read, and they have to be initialized in all branches of an if.

  • There's no way to specifiy that an object passed by reference to a function won't change for the duration of the function call. const T& t does not mean that the object pointed to by t won't change, but only that the reference t cannot be used to change it. The compiller still has to assume that any function call which it does not see into might change the object. It some cases, that prevents quite a few optimizations.


Another problem that has not been mentioned yet is the possibility for a badly designed interface to subvert const (even in the absence of casts).

Example:

class TreeNode {
public:
    TreeNode& getParent() const { return *parent_; }
    TreeNode& getLeft()   const { return *left_;   }
    TreeNode& getRight()  const { return *right_;  }
private:
    //TreeNode has a pointer to the Tree to enable navigation of the tree.
    //Assume that other design constraints mean that this must be a pointer 
    //rather than a reference.
    TreeNode* parent_;
    TreeNode* left_;
    TreeNode* right_;
};
//This function demonstrates the ability for const to be subverted.
TreeNode& remove_const(TreeNode const& toRemoveConstFrom) {
    TreeNode& parent(toRemoveConstFrom.getParent());
    TreeNode& leftChild(parent.getLeft());
    TreeNode& rightChild(parent.getRight());
    return &toRemoveConstFrom == &leftChild ? leftChild : rightChild;
}

The intransitive nature of const means that it is possible to have an interface where a non-const reference to an object can be obtained from a const reference to an object. This is something to be careful of when designing interfaces.


One problem is that the language also lets you const_cast it away, which defeats the purpose of using const in the first place.


One thing is that it is still possible subvert it. i.e. it is still legal to do something like this:

void foo(const inst& x)
{
   const_cast<int&> x = 3;
}

You can even use things like memset to subvert it without an explicit const_cast.

This is a trade-off between having the compiler enforce const and allowing some flexibility for non const aware interfaces.

Which leads to another limitation, in that it has not been universally embraced, which is in part due to another problem, which is that using const is an all-or-nothing proposition. If you start using it, you will need to propagate it throughout your code base.

0

精彩评论

暂无评论...
验证码 换一张
取 消