Click here to Skip to main content
Click here to Skip to main content

Constant Member Functions Are Not Always Constant

By , 24 May 2012
 

Herakleitos

Introduction

Recently, I had a discussion with my friends about constant member functions in C++ (for example void MyClass::foo const {...}) And one of my friends (he is really great at C++) said that if a member function is const-qualified, then we cannot modify any member variables in the scope of this function. Honestly, such a categorical opinion surprised me and I said, "it's not quite right!!" But all the participants in the debate supported my friend's view. This discussion prompted me to write a short article on the topic about how easy it is to modify the data members of your class in a const function of C++

Background

In fact, even one of the most-read books about C++, called "The C++ Programming Language, Third Edition" by Bjarne Stroustrup, says the following:

"10.2.6 Constant Member Functions [class.constmem]

   class Date 
   {
       int d, m, y;
       public :
          int day () const     { return  d; }
          int month () const { return m; }
          int year () const ;
       // ...
   };

Note the const after the (empty) argument list in the function declarations. It indicates that these functions do not modify the state of a Date.

Naturally, the compiler will catch accidental attempts to violate this promise. For example:

    inline int Date::year () const
    {
       return y ++; // error:  attempt to change member value in const function
    }
  
    When a const member function is defined outside its class, the const suffix is required:
    inline int Date::year () const
    {
       return y ; // correct
    }

And really, if your code tries to change some data member in a const function (by the above manner), you would get an error. When you develop an application by using Visual C++, you will receive the following message:

       error C3490: <your data member> cannot be modified because it is being accessed through a const object

I don't want to argue with the compiler (and even more so with the genial creator of C++) I only want to show another case ...

From my point of view, the author of the book didn't provide full disclosure of this subject, therefore, many programmers understand this subject insufficiently.

The Solution

I use a slightly different technique than the one showed by B.Stroustrup. My approach here allows you to use an interesting trick. The trick may look quite ugly, but it works ...

class User
{
  public:
	User(unsigned int id, unsigned int age, std::string name) 
		: mID(id) , 
		  mAge(age),
		  mName(name)
	{

	}

	void ChangeMe1 (unsigned int id, unsigned int age, std::string name) const;
	void ChangeMe2 (unsigned int id, unsigned int age, std::string name) const;
	void PrintMyData ();
private:
	unsigned int           mID;
	unsigned int           mAge;
        std::string            mName;
};

//To make changes by the pointer (a synonym on this)
void User::ChangeMe1 (unsigned int id, unsigned int age, std::string name) const
{
	// Here can be used and the old C-style cast to remove the constness, i.e.:
        // User * synonym_of_this =  (User *) this;
        User * synonym_of_this = const_cast<User *>(this);


	synonym_of_this->mID   = id; 
	synonym_of_this->mAge  = age;
	synonym_of_this->mName = name; 
}

//To make changes by creating the reference on this
void User::ChangeMe2 (unsigned int id, unsigned int age, std::string name) const
{
	// Here can be used and the old C-style cast to remove the constness, i.e.:
        // const UserPtr &synonym_of_this =  (User *) this;
        const UserPtr &synonym_of_this = const_cast<User *>(this);


	synonym_of_this->mID   = id; 
	synonym_of_this->mAge  = age;
	synonym_of_this->mName = name;
}

void User::PrintMyData ()
{
	std::cout << "ID = " << this->mID << " Age = " << this->mAge <<  " Name = " << this->mName.c_str() << "\n"; 
}

int main()
{
	User presedent_USA (42,46,"Bill Clinton");
	std::cout << "Now the presedent of USA : " << std::endl;
	presedent_USA.PrintMyData();
	
        std::cout << "\nLet's hold an election..." << std::endl;
	presedent_USA.ChangeMe1(43,54,"George Walker Bush");
	std::cout << "\nNow the presedent of USA : " << std::endl;
	presedent_USA.PrintMyData();

	std::cout << "\nLet's hold an election..." << std::endl;
	presedent_USA.ChangeMe2(44,47,"Barack Hussein Obama 2");
        std::cout << "\nNow the presedent of USA : " << std::endl;
	presedent_USA.PrintMyData();
	std::cout << "\n";

	return 0;
}

As you can be see from the above code example we continue to use the const-qualified function, but after starting the following program:

We can see that all members of this class have been changed, i.e. the ChangeMe1, and ChangeMe2 a "const" functions can modify any data members. Apparently the famous greek philosopher Heraclitus was right. In the ChangeMe1 function I used a "workaround" based on accessing data members through a pointer which is a synonym for this. The second solution used in ChangeMe2 is based on creating a reference to this, which behaves also as a synonym for this.

Why is it so important to consider?

  1. The const function can change the data members of class
    (for example, if you deal with scary legacy code and it's necessary to detect the place where some members of a class are changed, then do not ignore the code in the scope of a constant function, because there also may be changes)
  2. If you want to change the data members of a class (for some reason), but it's not possible to change the signature of a method, then you can use the "trick" that I showed. It is important to note that this should be done to resolve problems, but only in very extreme cases. It basically doesn't make any sense to cancel or violate the constancy of constant method, but if you are going to use this "trick", please write detailed comments, to be able to maintain your code. That's all!

History

  • 18nd April 2011: Initial post
  • License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    About the Author

    Volynsky Alex
    Software Developer
    Israel Israel
    Mr.Volynsky Alex Software Engineer in a leading software company. Alex is skilled in many areas of computer science. He has over 10 years of experience in the design and development of applications through C/C++/STL, Qt, MFC, COM/ActiveX, DirectShow, JavaScript, Tcl/Tk
    and of course - C#/.NET.
    Overall, Alex is very easy to work with. He adapts to new systems and technology while performing complete problem definition research.
     
    In his spare time, he enjoys programming in C# .NET , C++ maintaining projects free software projects. He is also fascinated by attending computer meetings in general and loves traveling.
     
    Visit his C++ 11 blog

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    GeneralMy vote of 5membermk4you713-Jun-12 5:00 
    Thanks a lot for sharing the article.
    Without this you can find yourself aimlessly tidying up and moving around code and not actually making things any better.
    Our gut often tells us that a piece of code is just bad, but by identifying a specific problem we have working with the code day to day we give ourselves more chance of success.
    GeneralRe: My vote of 5memberVolynsky Alex14-Jun-12 15:04 
    Thank you very much! Smile | :) Smile | :)
    ale44ko

    SuggestionMy vote of 5memberGrigor_I29-May-12 10:13 
    Yes, I have seen that trick many times.
    My team and support a large number of legacy applications all of which are currently functional but problematic to support and maintain
    Probably the most important thing to have when you begin refactoring legacy code is a know exactly the all features of C++ language
    GeneralRe: My vote of 5memberVolynsky Alex3-Jun-12 8:21 
    Thank you very much!Smile | :)
    ale44ko

    GeneralThe Const Nazi | Games from WithinmemberRolf Kristensen29-May-12 4:05 
    Funny article about const correctness The Const Nazi | Games from Within[^]
    GeneralRe: The Const Nazi | Games from WithinmemberVolynsky Alex3-Jun-12 8:22 
    Thumbs Up | :thumbsup:
    Thanks
    ale44ko

    GeneralMy vote of 5memberSteph_Iv28-May-12 10:36 
    I think it's very important to know for all students who going work with C ...
    Personally, if all autors made good content as you did, the internet will be much more …
    I’ve been browsing online more than three hours today, yet I never found any interesting article like yours. I searched the Internet answer to get rid of constancy... Because,I really can't change the interface...
    I'm surprised to see that.. it is so easy...
    Thanks a lot, this article was really helpful!
    GeneralRe: My vote of 5memberVolynsky Alex3-Jun-12 8:23 
    Thank you very much Steph_Iv! Smile | :)
    ale44ko

    GeneralMy vote of 5memberEMogilevsky26-May-12 4:21 
    Fine!!!
    But in these cases (the "Why is it so important to consider" section) can occur in different scenarios...
    The solution was simple and genial at the same time
    It's really help!
    Thanks, Alex
    GeneralRe: My vote of 5memberVolynsky Alex26-May-12 6:59 
    Thanks!!!
    ale44ko

    GeneralMy vote of 5memberGerard Forestier24-May-12 22:05 
    Very interesting points you have noted , thanks for putting up. “Success is a journey, not a destination. The doing is often more important than the outcome.” by Arthur Ashe.
    GeneralRe: My vote of 5memberVolynsky Alex26-May-12 7:00 
    Merci Gerard!
    ale44ko

    GeneralMy vote of 3memberPaul Watt23-May-12 13:39 
    I believe I understand your intention with the article and I agree it is a very important thing to teach programmers.
     
    However, I think you have simply scratched the surface on the topic, and your conclusion does not stand out very well compared to the majority of the content in the article.
     
    If you elaborate on some of the other topics that you have ignored, such as the mutable keyword and const_cast, as well as make your conclusion more the center of the article I will revote with a higher score.
     
    Thanks for bringing up this topic, it is definitely not something that is discussed much.
    GeneralRe: My vote of 5memberVolynsky Alex24-May-12 12:49 
    I corrected, as you requested (with const_cast)
    ale44ko

    GeneralMy vote of 1memberbitterskittles23-May-12 6:53 
    const_cast
    GeneralRe: My vote of 5memberVolynsky Alex23-May-12 8:35 
    Maybe.
    I correct it and let you know.Ok? Smile | :)
    ale44ko

    GeneralRe: My vote of 5memberVolynsky Alex24-May-12 12:40 
    I corrected, as you requested
    ale44ko

    GeneralMy vote of 5memberjfriedman23-May-12 4:16 
    The const decoration on the prototype is what allows this behavior at the Compiler and Machine level. Some of the comments indicate this. I vote 5 but argue to add an additional segment on building a call dynamically to be 'const' then to use a wrapper method such as '_CallMethodAsConst' to inspect the callee and apply the values or assign from optional given values required to facilitate the call.... This would be an interesting closing... Otherwise 5.
    AnswerRe: My vote of 5memberVolynsky Alex23-May-12 4:42 
    Great idea, thank you very much jfriedman!!! Smile | :) Smile | :) Smile | :)
    I will try to implement it ... I am sure it will be very useful
    Thank you for your openness!
     
    Best Regards,
    Alex.
    ale44ko

    QuestionMy vote of 5memberSharjith23-May-12 3:30 
    You have exposed and explained a caveat in a very nice way!
    RegardsSmile | :)
    N. Sharjith

    AnswerRe: My vote of 5 [modified]memberVolynsky Alex23-May-12 3:40 
    Dear friend! Smile | :) Smile | :)
    Thank you for your consideration and your understanding!
     
    Sincerely,
    Alex
     

    P.S.I'll be very grateful if you could to find time that would have read my other articles.
    ale44ko


    modified 23-May-12 9:56am.

    NewsMy vote of 5memberGlebGeglov23-May-12 0:22 
    Hi Alex - It's a great article - thank you for sharing it!
    I've seen a number of times this "trick" and it took me a long time to settle this problem
    Let's imagine if the constant function is not one line,for example 50 lines...
    will so difficult to detect it the changing of data members of class.
    Who will look for changes in const-function? But I assure you, they can be there!
    GeneralRe: My vote of 5memberVolynsky Alex23-May-12 1:15 
    Thank you very much, indeed!!!
    Many people don't understand....
    ale44ko

    GeneralMy vote of 1memberRolf Kristensen22-May-12 23:37 
    Really hate const_cast, especially when not using the keyword const_cast.
     
    I barely accept mutable variables for handling caching of calculated values.
     
    Readability and multithreading is out of the window when using such hacks.
    GeneralRe: My vote of 5memberVolynsky Alex22-May-12 23:40 
    Smile | :) It is most important pay attention to concept of this article !!!
    Read again what I wrote in the section: "Why is it so important to consider?"
    ale44ko

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Permalink | Advertise | Privacy | Mobile
    Web04 | 2.6.130617.1 | Last Updated 24 May 2012
    Article Copyright 2012 by Volynsky Alex
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid