• Sign In / Suggest an Article

Current ISO C++ status

Upcoming ISO C++ meetings

Upcoming C++ conferences

Compiler conformance status

C++ (on Sea) Online

February 7-9

ISO C++ committee meeting

March 18-23, Tokyo, Japan

April 17-20, Bristol, UK

using std::cpp 2024

April 24-26, Leganes, Spain  

C++ Now 2024

May 7-12, Aspen, CO, USA

June 24-29, St. Louis, MO, USA

July 2-5, Folkestone, Kent, UK

assignment operators

Assignment operators, what is “self assignment”.

Self assignment is when someone assigns an object to itself. For example,

Obviously no one ever explicitly does a self assignment like the above, but since more than one pointer or reference can point to the same object (aliasing), it is possible to have self assignment without knowing it:

This is only valid for copy assignment. Self-assignment is not valid for move assignment.

Why should I worry about “self assignment”?

If you don’t worry about self assignment , you’ll expose your users to some very subtle bugs that have very subtle and often disastrous symptoms. For example, the following class will cause a complete disaster in the case of self-assignment:

If someone assigns a Fred object to itself, line #1 deletes both this->p_ and f.p_ since *this and f are the same object. But line #2 uses *f.p_ , which is no longer a valid object. This will likely cause a major disaster.

The bottom line is that you the author of class Fred are responsible to make sure self-assignment on a Fred object is innocuous . Do not assume that users won’t ever do that to your objects. It is your fault if your object crashes when it gets a self-assignment.

Aside: the above Fred::operator= (const Fred&) has a second problem: If an exception is thrown while evaluating new Wilma(*f.p_) (e.g., an out-of-memory exception or an exception in Wilma ’s copy constructor ), this->p_ will be a dangling pointer — it will point to memory that is no longer valid. This can be solved by allocating the new objects before deleting the old objects.

Okay, okay, already; I’ll handle self-assignment. How do I do it?

You should worry about self assignment every time you create a class . This does not mean that you need to add extra code to all your classes: as long as your objects gracefully handle self assignment, it doesn’t matter whether you had to add extra code or not.

We will illustrate the two cases using the assignment operator in the previous FAQ :

If self-assignment can be handled without any extra code, don’t add any extra code. But do add a comment so others will know that your assignment operator gracefully handles self-assignment:

Example 1a:

Example 1b:

If you need to add extra code to your assignment operator, here’s a simple and effective technique:

Or equivalently:

By the way: the goal is not to make self-assignment fast. If you don’t need to explicitly test for self-assignment, for example, if your code works correctly (even if slowly) in the case of self-assignment, then do not put an if test in your assignment operator just to make the self-assignment case fast. The reason is simple: self-assignment is almost always rare, so it merely needs to be correct - it does not need to be efficient. Adding the unnecessary if statement would make a rare case faster by adding an extra conditional-branch to the normal case, punishing the many to benefit the few.

In this case, however, you should add a comment at the top of your assignment operator indicating that the rest of the code makes self-assignment is benign, and that is why you didn’t explicitly test for it. That way future maintainers will know to make sure self-assignment stays benign, or if not, they will need to add the if test.

I’m creating a derived class; should my assignment operators call my base class’s assignment operators?

Yes (if you need to define assignment operators in the first place).

If you define your own assignment operators, the compiler will not automatically call your base class’s assignment operators for you. Unless your base class’s assignment operators themselves are broken, you should call them explicitly from your derived class’s assignment operators (again, assuming you create them in the first place).

However if you do not create your own assignment operators, the ones that the compiler create for you will automatically call your base class’s assignment operators.

Please Login to submit a recommendation.

If you don’t have an account, you can register for free.

C++ Operator Overloading Guidelines

One of the nice features of C++ is that you can give special meanings to operators, when they are used with user-defined classes. This is called operator overloading . You can implement C++ operator overloads by providing special member-functions on your classes that follow a particular naming convention. For example, to overload the + operator for your class, you would provide a member-function named operator+ on your class.

  • = (assignment operator)
  • + - * (binary arithmetic operators)
  • += -= *= (compound assignment operators)
  • == != (comparison operators)

Here are some guidelines for implementing these operators. These guidelines are very important to follow, so definitely get in the habit early.

Assignment Operator =

The assignment operator has a signature like this: class MyClass { public: ... MyClass & operator=(const MyClass &rhs); ... } MyClass a, b; ... b = a; // Same as b.operator=(a);

Notice that the = operator takes a const-reference to the right hand side of the assignment. The reason for this should be obvious, since we don't want to change that value; we only want to change what's on the left hand side.

  • e = 42 assigns 42 to e , then returns e as the result
  • The value of e is then assigned to d , and then d is returned as the result
  • The value of d is then assigned to c , and then c is returned as the result

Now, in order to support operator chaining, the assignment operator must return some value. The value that should be returned is a reference to the left-hand side of the assignment.

Notice that the returned reference is not declared const . This can be a bit confusing, because it allows you to write crazy stuff like this: MyClass a, b, c; ... (a = b) = c; // What?? At first glance, you might want to prevent situations like this, by having operator= return a const reference. However, statements like this will work with primitive types. And, even worse, some tools actually rely on this behavior. Therefore, it is important to return a non- const reference from your operator= . The rule of thumb is, "If it's good enough for int s, it's good enough for user-defined data-types."

So, for the hypothetical MyClass assignment operator, you would do something like this: // Take a const-reference to the right-hand side of the assignment. // Return a non-const reference to the left-hand side. MyClass& MyClass::operator=(const MyClass &rhs) { ... // Do the assignment operation! return *this; // Return a reference to myself. } Remember, this is a pointer to the object that the member function is being called on. Since a = b is treated as a.operator=(b) , you can see why it makes sense to return the object that the function is called on; object a is the left-hand side.

But, the member function needs to return a reference to the object, not a pointer to the object. So, it returns *this , which returns what this points at (i.e. the object), not the pointer itself. (In C++, instances are turned into references, and vice versa, pretty much automatically, so even though *this is an instance, C++ implicitly converts it into a reference to the instance.)

Now, one more very important point about the assignment operator:

YOU MUST CHECK FOR SELF-ASSIGNMENT!

This is especially important when your class does its own memory allocation. Here is why: The typical sequence of operations within an assignment operator is usually something like this: MyClass& MyClass::operator=(const MyClass &rhs) { // 1. Deallocate any memory that MyClass is using internally // 2. Allocate some memory to hold the contents of rhs // 3. Copy the values from rhs into this instance // 4. Return *this } Now, what happens when you do something like this: MyClass mc; ... mc = mc; // BLAMMO. You can hopefully see that this would wreak havoc on your program. Because mc is on the left-hand side and on the right-hand side, the first thing that happens is that mc releases any memory it holds internally. But, this is where the values were going to be copied from, since mc is also on the right-hand side! So, you can see that this completely messes up the rest of the assignment operator's internals.

The easy way to avoid this is to CHECK FOR SELF-ASSIGNMENT. There are many ways to answer the question, "Are these two instances the same?" But, for our purposes, just compare the two objects' addresses. If they are the same, then don't do assignment. If they are different, then do the assignment.

So, the correct and safe version of the MyClass assignment operator would be this: MyClass& MyClass::operator=(const MyClass &rhs) { // Check for self-assignment! if (this == &rhs) // Same object? return *this; // Yes, so skip assignment, and just return *this. ... // Deallocate, allocate new space, copy values... return *this; } Or, you can simplify this a bit by doing: MyClass& MyClass::operator=(const MyClass &rhs) { // Only do assignment if RHS is a different object from this. if (this != &rhs) { ... // Deallocate, allocate new space, copy values... } return *this; } Remember that in the comparison, this is a pointer to the object being called, and &rhs is a pointer to the object being passed in as the argument. So, you can see that we avoid the dangers of self-assignment with this check.

  • Take a const-reference for the argument (the right-hand side of the assignment).
  • Return a reference to the left-hand side, to support safe and reasonable operator chaining. (Do this by returning *this .)
  • Check for self-assignment, by comparing the pointers ( this to &rhs ).

Compound Assignment Operators += -= *=

I discuss these before the arithmetic operators for a very specific reason, but we will get to that in a moment. The important point is that these are destructive operators, because they update or replace the values on the left-hand side of the assignment. So, you write: MyClass a, b; ... a += b; // Same as a.operator+=(b) In this case, the values within a are modified by the += operator.

How those values are modified isn't very important - obviously, what MyClass represents will dictate what these operators mean.

The member function signature for such an operator should be like this: MyClass & MyClass::operator+=(const MyClass &rhs) { ... } We have already covered the reason why rhs is a const-reference. And, the implementation of such an operation should also be straightforward.

But, you will notice that the operator returns a MyClass -reference, and a non-const one at that. This is so you can do things like this: MyClass mc; ... (mc += 5) += 3;

Don't ask me why somebody would want to do this, but just like the normal assignment operator, this is allowed by the primitive data types. Our user-defined datatypes should match the same general characteristics of the primitive data types when it comes to operators, to make sure that everything works as expected.

This is very straightforward to do. Just write your compound assignment operator implementation, and return *this at the end, just like for the regular assignment operator. So, you would end up with something like this: MyClass & MyClass::operator+=(const MyClass &rhs) { ... // Do the compound assignment work. return *this; }

As one last note, in general you should beware of self-assignment with compound assignment operators as well. Fortunately, none of the C++ track's labs require you to worry about this, but you should always give it some thought when you are working on your own classes.

Binary Arithmetic Operators + - *

The binary arithmetic operators are interesting because they don't modify either operand - they actually return a new value from the two arguments. You might think this is going to be an annoying bit of extra work, but here is the secret:

Define your binary arithmetic operators using your compound assignment operators.

There, I just saved you a bunch of time on your homeworks.

So, you have implemented your += operator, and now you want to implement the + operator. The function signature should be like this: // Add this instance's value to other, and return a new instance // with the result. const MyClass MyClass::operator+(const MyClass &other) const { MyClass result = *this; // Make a copy of myself. Same as MyClass result(*this); result += other; // Use += to add other to the copy. return result; // All done! } Simple!

Actually, this explicitly spells out all of the steps, and if you want, you can combine them all into a single statement, like so: // Add this instance's value to other, and return a new instance // with the result. const MyClass MyClass::operator+(const MyClass &other) const { return MyClass(*this) += other; } This creates an unnamed instance of MyClass , which is a copy of *this . Then, the += operator is called on the temporary value, and then returns it.

If that last statement doesn't make sense to you yet, then stick with the other way, which spells out all of the steps. But, if you understand exactly what is going on, then you can use that approach.

You will notice that the + operator returns a const instance, not a const reference. This is so that people can't write strange statements like this: MyClass a, b, c; ... (a + b) = c; // Wuh...? This statement would basically do nothing, but if the + operator returns a non- const value, it will compile! So, we want to return a const instance, so that such madness will not even be allowed to compile.

  • Implement the compound assignment operators from scratch, and then define the binary arithmetic operators in terms of the corresponding compound assignment operators.
  • Return a const instance, to prevent worthless and confusing assignment operations that shouldn't be allowed.

Comparison Operators == and !=

The comparison operators are very simple. Define == first, using a function signature like this: bool MyClass::operator==(const MyClass &other) const { ... // Compare the values, and return a bool result. } The internals are very obvious and straightforward, and the bool return-value is also very obvious.

The important point here is that the != operator can also be defined in terms of the == operator, and you should do this to save effort. You can do something like this: bool MyClass::operator!=(const MyClass &other) const { return !(*this == other); } That way you get to reuse the hard work you did on implementing your == operator. Also, your code is far less likely to exhibit inconsistencies between == and != , since one is implemented in terms of the other.

Assignment operators

Assignment operators modify the value of the object.

Explanation

copy assignment operator replaces the contents of the object a with a copy of the contents of b ( b is not modified). For class types, this is a special member function, described in copy assignment operator .

move assignment operator replaces the contents of the object a with the contents of b , avoiding copying if possible ( b may be modified). For class types, this is a special member function, described in move assignment operator . (since C++11)

For non-class types, copy and move assignment are indistinguishable and are referred to as direct assignment .

compound assignment operators replace the contents of the object a with the result of a binary operation between the previous value of a and the value of b .

Builtin direct assignment

The direct assignment expressions have the form

For the built-in operator, lhs may have any non-const scalar type and rhs must be implicitly convertible to the type of lhs .

The direct assignment operator expects a modifiable lvalue as its left operand and an rvalue expression or a braced-init-list (since C++11) as its right operand, and returns an lvalue identifying the left operand after modification.

For non-class types, the right operand is first implicitly converted to the cv-unqualified type of the left operand, and then its value is copied into the object identified by left operand.

When the left operand has reference type, the assignment operator modifies the referred-to object.

If the left and the right operands identify overlapping objects, the behavior is undefined (unless the overlap is exact and the type is the same)

In overload resolution against user-defined operators , for every type T , the following function signatures participate in overload resolution:

For every enumeration or pointer to member type T , optionally volatile-qualified, the following function signature participates in overload resolution:

For every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signature participates in overload resolution:

Builtin compound assignment

The compound assignment expressions have the form

The behavior of every builtin compound-assignment expression E1 op = E2 (where E1 is a modifiable lvalue expression and E2 is an rvalue expression or a braced-init-list (since C++11) ) is exactly the same as the behavior of the expression E1 = E1 op E2 , except that the expression E1 is evaluated only once and that it behaves as a single operation with respect to indeterminately-sequenced function calls (e.g. in f ( a + = b, g ( ) ) , the += is either not started at all or is completed as seen from inside g ( ) ).

In overload resolution against user-defined operators , for every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signatures participate in overload resolution:

For every pair I1 and I2, where I1 is an integral type (optionally volatile-qualified) and I2 is a promoted integral type, the following function signatures participate in overload resolution:

For every optionally cv-qualified object type T , the following function signatures participate in overload resolution:

Operator precedence

Operator overloading

  • C++ Data Types

C++ Input/Output

  • C++ Pointers

C++ Interview Questions

  • C++ Programs
  • C++ Cheatsheet
  • C++ Projects

C++ Exception Handling

  • C++ Memory Management

Related Articles

  • Solve Coding Problems
  • C++ Programming Language

C++ Overview

  • Introduction to C++ Programming Language
  • Features of C++
  • History of C++
  • Interesting Facts about C++
  • Setting up C++ Development Environment
  • Difference between C and C++
  • Writing First C++ Program - Hello World Example
  • C++ Basic Syntax
  • C++ Comments
  • Tokens in C
  • C++ Keywords
  • Difference between Keyword and Identifier

C++ Variables and Constants

  • C++ Variables
  • Constants in C
  • Scope of Variables in C++
  • Storage Classes in C++ with Examples
  • Static Keyword in C++

C++ Data Types and Literals

  • Literals in C
  • Derived Data Types in C++
  • User Defined Data Types in C++
  • Data Type Ranges and their macros in C++
  • C++ Type Modifiers
  • Type Conversion in C++
  • Casting Operators in C++

C++ Operators

  • Operators in C++
  • C++ Arithmetic Operators
  • Unary operators in C
  • Bitwise Operators in C
  • Assignment Operators in C
  • C++ sizeof Operator

Scope resolution operator in C++

  • Basic Input / Output in C++
  • cout in C++
  • cerr - Standard Error Stream Object in C++
  • Manipulators in C++ with Examples

C++ Control Statements

  • Decision Making in C (if , if..else, Nested if, if-else-if )
  • C++ if Statement
  • C++ if else Statement
  • C++ if else if Ladder
  • Switch Statement in C++
  • Jump statements in C++
  • for Loop in C++
  • Range-based for loop in C++
  • C++ While Loop
  • C++ Do/While Loop

C++ Functions

  • Functions in C++
  • return statement in C++ with Examples
  • Parameter Passing Techniques in C
  • Difference Between Call by Value and Call by Reference in C
  • Default Arguments in C++
  • Inline Functions in C++
  • Lambda expression in C++

C++ Pointers and References

  • Pointers and References in C++
  • Dangling, Void , Null and Wild Pointers in C
  • Applications of Pointers in C
  • Understanding nullptr in C++
  • References in C++
  • Can References Refer to Invalid Location in C++?
  • Pointers vs References in C++
  • Passing By Pointer vs Passing By Reference in C++
  • When do we pass arguments by pointer?
  • Variable Length Arrays (VLAs) in C
  • Pointer to an Array | Array Pointer
  • How to print size of array parameter in C++?
  • Pass Array to Functions in C
  • What is Array Decay in C++? How can it be prevented?

C++ Strings

  • Strings in C++
  • std::string class in C++
  • Array of Strings in C++ - 5 Different Ways to Create
  • String Concatenation in C++
  • Tokenizing a string in C++
  • Substring in C++

C++ Structures and Unions

  • Structures, Unions and Enumerations in C++
  • Structures in C++
  • C++ - Pointer to Structure
  • Self Referential Structures
  • Difference Between C Structures and C++ Structures
  • Enumeration in C++
  • typedef in C++
  • Array of Structures vs Array within a Structure in C

C++ Dynamic Memory Management

  • Dynamic Memory Allocation in C using malloc(), calloc(), free() and realloc()
  • new and delete Operators in C++ For Dynamic Memory
  • new vs malloc() and free() vs delete in C++
  • What is Memory Leak? How can we avoid?
  • Difference between Static and Dynamic Memory Allocation in C

C++ Object-Oriented Programming

  • Object Oriented Programming in C++
  • C++ Classes and Objects
  • Access Modifiers in C++
  • Friend Class and Function in C++
  • Constructors in C++
  • Default Constructors in C++
  • Copy Constructor in C++
  • Destructors in C++
  • Private Destructor in C++
  • When is a Copy Constructor Called in C++?
  • Shallow Copy and Deep Copy in C++
  • When Should We Write Our Own Copy Constructor in C++?
  • Does C++ compiler create default constructor when we write our own?
  • C++ Static Data Members
  • Static Member Function in C++
  • 'this' pointer in C++
  • Scope Resolution Operator vs this pointer in C++
  • Local Classes in C++
  • Nested Classes in C++
  • Enum Classes in C++ and Their Advantage over Enum DataType
  • Difference Between Structure and Class in C++
  • Why C++ is partially Object Oriented Language?

C++ Encapsulation and Abstraction

  • Encapsulation in C++
  • Abstraction in C++
  • Difference between Abstraction and Encapsulation in C++
  • C++ Polymorphism
  • Function Overriding in C++
  • Virtual Functions and Runtime Polymorphism in C++
  • Difference between Inheritance and Polymorphism

C++ Function Overloading

  • Function Overloading in C++
  • Constructor Overloading in C++
  • Functions that cannot be overloaded in C++
  • Function overloading and const keyword
  • Function Overloading and Return Type in C++
  • Function Overloading and float in C++
  • C++ Function Overloading and Default Arguments
  • Can main() be overloaded in C++?
  • Function Overloading vs Function Overriding in C++
  • Advantages and Disadvantages of Function Overloading in C++

C++ Operator Overloading

  • Operator Overloading in C++
  • Types of Operator Overloading in C++
  • Functors in C++
  • What are the Operators that Can be and Cannot be Overloaded in C++?

C++ Inheritance

  • Inheritance in C++
  • C++ Inheritance Access
  • Multiple Inheritance in C++
  • C++ Hierarchical Inheritance
  • C++ Multilevel Inheritance
  • Constructor in Multiple Inheritance in C++
  • Inheritance and Friendship in C++
  • Does overloading work with Inheritance?

C++ Virtual Functions

  • Virtual Function in C++
  • Virtual Functions in Derived Classes in C++
  • Default Arguments and Virtual Function in C++
  • Can Virtual Functions be Inlined in C++?
  • Virtual Destructor
  • Advanced C++ | Virtual Constructor
  • Advanced C++ | Virtual Copy Constructor
  • Pure Virtual Functions and Abstract Classes in C++
  • Pure Virtual Destructor in C++
  • Can Static Functions Be Virtual in C++?
  • RTTI (Run-Time Type Information) in C++
  • Can Virtual Functions be Private in C++?
  • Exception Handling in C++
  • Exception Handling using classes in C++
  • Stack Unwinding in C++
  • User-defined Custom Exception with class in C++

C++ Files and Streams

  • File Handling through C++ Classes
  • I/O Redirection in C++

C++ Templates

  • Templates in C++ with Examples
  • Template Specialization in C++
  • Using Keyword in C++ STL

C++ Standard Template Library (STL)

  • The C++ Standard Template Library (STL)
  • Containers in C++ STL (Standard Template Library)
  • Introduction to Iterators in C++
  • Algorithm Library | C++ Magicians STL Algorithm

C++ Preprocessors

  • C Preprocessors
  • C Preprocessor Directives
  • #include in C
  • Difference between Preprocessor Directives and Function Templates in C++

C++ Namespace

  • Namespace in C++ | Set 1 (Introduction)
  • namespace in C++ | Set 2 (Extending namespace and Unnamed namespace)
  • Namespace in C++ | Set 3 (Accessing, creating header, nesting and aliasing)
  • C++ Inline Namespaces and Usage of the "using" Directive Inside Namespaces

Advanced C++

  • Multithreading in C++
  • Smart Pointers in C++
  • auto_ptr vs unique_ptr vs shared_ptr vs weak_ptr in C++
  • Type of 'this' Pointer in C++
  • "delete this" in C++
  • Passing a Function as a Parameter in C++
  • Signal Handling in C++
  • Generics in C++
  • Difference between C++ and Objective C
  • Write a C program that won't compile in C++
  • Write a program that produces different results in C and C++
  • How does 'void*' differ in C and C++?
  • Type Difference of Character Literals in C and C++
  • Cin-Cout vs Scanf-Printf

C++ vs Java

  • Similarities and Difference between Java and C++
  • Comparison of Inheritance in C++ and Java
  • How Does Default Virtual Behavior Differ in C++ and Java?
  • Comparison of Exception Handling in C++ and Java
  • Foreach in C++ and Java
  • Templates in C++ vs Generics in Java
  • Floating Point Operations & Associativity in C, C++ and Java

Competitive Programming in C++

  • Competitive Programming - A Complete Guide
  • C++ tricks for competitive programming (for C++ 11)
  • Writing C/C++ code efficiently in Competitive programming
  • Why C++ is best for Competitive Programming?
  • Test Case Generation | Set 1 (Random Numbers, Arrays and Matrices)
  • Fast I/O for Competitive Programming
  • Setting up Sublime Text for C++ Competitive Programming Environment
  • How to setup Competitive Programming in Visual Studio Code for C++
  • Which C++ libraries are useful for competitive programming?
  • Common mistakes to be avoided in Competitive Programming in C++ | Beginners
  • C++ Interview Questions and Answers (2024)
  • Top C++ STL Interview Questions and Answers
  • 30 OOPs Interview Questions and Answers (2024)
  • Top C++ Exception Handling Interview Questions and Answers
  • C++ Programming Examples

In C++, the scope resolution operator is :: . It is used for the following purposes.

1) To access a global variable when there is a local variable with same name:  

2) To define a function outside a class.  

3) To access a class’s static variables.  

4) In case of multiple Inheritance: If the same variable name exists in two ancestor classes, we can use scope resolution operator to distinguish. 

5) For namespace If a class having the same name exists inside two namespaces we can use the namespace name with the scope resolution operator to refer that class without any conflicts

6) Refer to a class inside another class: If a class exists inside another class we can use the nesting class to refer the nested class using the scope resolution operator

Please Login to comment...

  • kumawatlalit912
  • chawlaharsh05
  • Google Introduces "Genie AI": AI Platform That Lets You Create Video Games
  • IBM and Samsung SDS Transform Enterprise Mobility with Zero-Touch Technology
  • Tech Layoffs Continue as Cisco Plans to Cut 700 More Jobs
  • Best 15 Tools to Create Faceless YouTube Videos in 2024
  • Dev Scripter 2024 - Biggest Technical Writing Event By GeeksforGeeks

Improve your Coding Skills with Practice

 alt=

What kind of Experience do you want to share?

Learn C++

21.9 — Overloading the subscript operator

When working with arrays, we typically use the subscript operator ([]) to index specific elements of an array:

However, consider the following IntList class, which has a member variable that is an array:

Because the m_list member variable is private, we can not access it directly from variable list. This means we have no way to directly get or set values in the m_list array. So how do we get or put elements into our list?

Without operator overloading, the typical method would be to create access functions:

While this works, it’s not particularly user friendly. Consider the following example:

Are we setting element 2 to the value 3, or element 3 to the value 2? Without seeing the definition of setItem() , it’s simply not clear.

You could also just return the entire list and use operator[] to access the element:

While this also works, it’s syntactically odd:

Overloading operator[]

However, a better solution in this case is to overload the subscript operator ([]) to allow access to the elements of m_list. The subscript operator is one of the operators that must be overloaded as a member function. An overloaded operator[] function will always take one parameter: the subscript that the user places between the hard braces. In our IntList case, we expect the user to pass in an integer index, and we’ll return an integer value back as a result.

Now, whenever we use the subscript operator ([]) on an object of our class, the compiler will return the corresponding element from the m_list member variable! This allows us to both get and set values of m_list directly.

This is both easy syntactically and from a comprehension standpoint. When list[2] evaluates, the compiler first checks to see if there’s an overloaded operator[] function. If so, it passes the value inside the hard braces (in this case, 2) as an argument to the function.

Note that although you can provide a default value for the function parameter, actually using operator[] without a subscript inside is not considered a valid syntax, so there’s no point.

C++23 adds support for overloading operator[] with multiple subscripts.

Why operator[] returns a reference

Let’s take a closer look at how list[2] = 3 evaluates. Because the subscript operator has a higher precedence than the assignment operator, list[2] evaluates first. list[2] calls operator[], which we’ve defined to return a reference to list.m_list[2] . Because operator[] is returning a reference, it returns the actual list.m_list[2] array element. Our partially evaluated expression becomes list.m_list[2] = 3 , which is a straightforward integer assignment.

In lesson 12.2 -- Value categories (lvalues and rvalues) , you learned that any value on the left hand side of an assignment statement must be an l-value (which is a variable that has an actual memory address). Because the result of operator[] can be used on the left hand side of an assignment (e.g. list[2] = 3 ), the return value of operator[] must be an l-value. As it turns out, references are always l-values, because you can only take a reference of variables that have memory addresses. So by returning a reference, the compiler is satisfied that we are returning an l-value.

Consider what would happen if operator[] returned an integer by value instead of by reference. list[2] would call operator[], which would return the value of list.m_list[2]. For example, if m_list[2] had the value of 6, operator[] would return the value 6. list[2] = 3 would partially evaluate to 6 = 3 , which makes no sense! If you try to do this, the C++ compiler will complain:

Overloaded operator[] for const objects

In the above IntList example, operator[] is non-const, and we can use it as an l-value to change the state of non-const objects. However, what if our IntList object was const? In this case, we wouldn’t be able to call the non-const version of operator[] because that would allow us to potentially change the state of a const object.

The good news is that we can define a non-const and a const version of operator[] separately. The non-const version will be used with non-const objects, and the const version with const-objects.

Removing duplicate code between const and non-const overloads

In the above example, note that the implementations of int& IntList::operator[](int) and const int& IntList::operator[](int) const are identical. The only difference is the return type of the function.

In cases where the implementation is trivial (e.g. a single line), it’s fine (and preferred) to have both functions use an identical implementation. The small amount of redundancy this introduces isn’t worth removing.

But what if the implementation of these operators was complex, requiring many statements? For example, maybe it’s important that we validate that the index is actually valid, which requires adding many redundant lines of code to each function.

In such a case, the redundancy introduced by having many duplicate statements is more problematic, and it would be desirable to have a single implementation that we could use for both overloads. But how? Normally we’d simply implement one function in terms of the other (e.g. have one function call the other). But that’s a bit tricky in this case. The const version of the function can’t call the non-const version of the function, because that would require discarding the const of a const object. And while the non-const version of the function can call the const version of the function, the const version of the function returns a const reference, when we need to return a non-const reference. Fortunately, there is a way to work around this.

The preferred solution is as follows:

  • Refactor the logic into another function (usually a private const member function, but could be a non-member function).
  • Have the non-const function call the const function and use const_cast to remove the const of the returned reference.

The resulting solution looks something like this:

Normally using const_cast to remove const is something we want to avoid, but in this case it’s acceptable. If the non-const overload was called, then we know we’re working with a non-const object. It’s okay to remove the const on a const reference to a non-const object.

For advanced readers

In C++23, we can do even better by making use of several features we don’t yet cover in this tutorial series:

Detecting index validity

One other advantage of overloading the subscript operator is that we can make it safer than accessing arrays directly. Normally, when accessing arrays, the subscript operator does not check whether the index is valid. For example, the compiler will not complain about the following code:

However, if we know the size of our array, we can make our overloaded subscript operator check to ensure the index is within bounds:

In the above example, we have used the assert() function (included in the cassert header) to make sure our index is valid. If the expression inside the assert evaluates to false (which means the user passed in an invalid index), the program will terminate with an error message, which is much better than the alternative (corrupting memory). This is probably the most common method of doing error checking of this sort.

If you don’t want to use an assert (which will be compiled out of a non-debug build) you can instead use an if-statement and your favorite error handling method (e.g. throw an exception, call std::exit , etc…):

Pointers to objects and overloaded operator[] don’t mix

If you try to call operator[] on a pointer to an object, C++ will assume you’re trying to index an array of objects of that type.

Consider the following example:

Because we can’t assign an integer to an IntList, this won’t compile. However, if assigning an integer was valid, this would compile and run, with undefined results.

Make sure you’re not trying to call an overloaded operator[] on a pointer to an object.

The proper syntax would be to dereference the pointer first (making sure to use parenthesis since operator[] has higher precedence than operator*), then call operator[]:

This is ugly and error prone. Better yet, don’t set pointers to your objects if you don’t have to.

The function parameter does not need to be an integral type

As mentioned above, C++ passes what the user types between the hard braces as an argument to the overloaded function. In most cases, this will be an integral value. However, this is not required -- and in fact, you can define that your overloaded operator[] take a value of any type you desire. You could define your overloaded operator[] to take a double, a std::string, or whatever else you like.

As a ridiculous example, just so you can see that it works:

As you would expect, this prints:

Overloading operator[] to take a std::string parameter can be useful when writing certain kinds of classes, such as those that use words as indices.

Question #1

A map is a class that stores elements as a key-value pair. The key must be unique, and is used to access the associated pair. In this quiz, we’re going to write an application that lets us assign grades to students by name, using a simple map class. The student’s name will be the key, and the grade (as a char) will be the value.

a) First, write a struct named StudentGrade that contains the student’s name (as a std::string ) and grade (as a char ).

Show Solution

b) Add a class named GradeMap that contains a std::vector of StudentGrade named m_map .

c) Write an overloaded operator[] for this class. This function should take a std::string parameter, and return a reference to a char. In the body of the function, first see if the student’s name already exists (You can use std::find_if from <algorithm>). If the student exists, return a reference to the grade and you’re done. Otherwise, use the std::vector::emplace_back() function to add a StudentGrade for this new student. When you do this, std::vector will add a copy of your StudentGrade to itself (resizing if needed, invalidating all previously returned references). Finally, we need to return a reference to the grade for the student we just added to the std::vector . We can access the student we just added using the std::vector::back() function.

The following program should run:

See 20.6 -- Introduction to lambdas (anonymous functions) for more information on lambdas.

Since maps are common, the standard library offers std::map , which is not currently covered on learncpp. Using std::map , we can simplify our code to

Prefer using std::map over writing your own implementation.

Question #2

Extra credit #1: The GradeMap class and sample program we wrote is inefficient for many reasons. Describe one way that the GradeMap class could be improved.

std::vector is unsorted by nature. This means every time we call operator[] , we’re potentially traversing the entire std::vector to find our element. With a few elements, this isn’t a problem, but as we continue to add names, this will become increasingly slow. We could optimize this by keeping our m_map sorted and using a binary search, so we minimize the number of elements we have to look through to find the ones we’re interested in.

Question #3

Extra credit #2: Why does this program potentially not work as expected?

When Frank is added, the std::vector may need to grow to hold it. This requires dynamically allocating a new block of memory, copying the elements in the array to that new block, and deleting the old block. When this happens, any references to existing elements in the std::vector are invalidated (meaning they are left as dangling references to deleted memory).

In other words, after we emplace_back("Frank") , if the std::vector had to grow to make room for Frank, the gradeJoe reference would be invalidated. Accessing gradeJoe to print Joe’s grade would then lead to undefined results.

How a std::vector grows is a compiler-specific detail, so we can expect the above program to work fine when compiled with some compilers and not others.

guest

  • Operating Systems & Software
  • Programmer's Symposium

[C++] Why can't I externally overload operator= with my class on the RHS?

  • Thread starter .mobius
  • Start date Jun 25, 2003

More options

Ars scholae palatinae.

  • Jun 25, 2003
  • Add bookmark

I'm writing a wrapped data class to allow me to easily convert between several similar datatypes. That is, I have a class MyClass which internally represents an item, but can interact with ints, floats, doubles, all in predefined ways. This is a consummate example of why operator overloading is a Good Thing, right? I can write:<BR><pre class="ip-ubbcode-code-pre"> resultInt = anInt + MyInstance; resultFloat = aFloat + MyInstance; </pre><BR>and it all works transparently to the user of the class. <B>But</B>, I can't do what I want with assignment. For example:<BR><pre class="ip-ubbcode-code-pre"> myInt = myInstance; // All good and happy and right. myInstance = myInt; // Apparently not allowed. (Why not?). </pre><BR><BR>To do the second, one would naturally define <pre class="ip-ubbcode-code-pre">operator= (int lhs, const MyClass& rhs);</pre> externally and include it as a friend of MyClass. However, it seems this is not allowed (at least, according to VC++ 6 docs; I haven't tried GCC yet).<BR><BR>Why is this not allowed? How should I handle it instead? I'm at a loss, because I can't think of any other natural way to write this, and I can't find any info online about how to make it work.<BR><BR>Thanks!<BR><BR><BR>(.mobius)<BR><BR>[This message was edited by .mobius on June 25, 2003 at 15:13.]  

Well, not being a language lawyer, I can't say much here, though perhaps this somehow conflicts with the copy constructor?<BR><BR>OT: In doing some googling on this, I did come across an interesting paper by Stroustrup on generalized overloading... PDF  

Ars Centurion

Try adding a cast operator to your class...<BR><BR>Like:<BR><BR><pre class="ip-ubbcode-code-pre"> #include <iostream> using namespace std; class MyInt { public: MyInt(int a) : _a(a) { } operator int() { return _a; } private: int _a; }; int main(int argc, char* argv[]) { MyInt a(7); cout << (int) a << endl; return 0; } </pre>  

The problem isn't that I specifically am having an issue making it work correctly. The problem is that it is illegal in C++ to have an external operator definition for =, according to VC++ docs.<BR><BR>I'm curious as to why they made this illegal, since it would seem to be a useful thing to be able to do, and how to get around it, because I can't imagine I'm the first person to look at this and think "it sure would be nice if I could overload my = operator so that I can set some primitive type equal to my class"--more importantly, I'm certainly not the most ingenious or experienced C++ programmer to come across this.<BR><BR>EDIT: Hyper165, you posted while I was replying.<BR><BR>That's interesting. Do I need to have MyInt::int() defined as a public method for that to work?<BR>I'll try it out. It seems to be what I want, but it seems like an odd way to do it.<BR><BR>Thanks!<BR><BR>EDIT again:<BR>I misread that. You're overloading the (int) casting operator. That makes a whole lot more sense now.<BR><BR>[This message was edited by .mobius on June 25, 2003 at 15:59.]  

Perhaps I misunderstood your question, but I don't quite see how that code helps with<BR><BR><pre class="ip-ubbcode-code-pre"> myInstance = myInt; // Apparently not allowed. (Why not?). </pre><BR><BR>Assuming that myInstance is your class, and myInt is just a regular int.<BR><BR>Of course, if you're happy wrapping your ints in a MyInt class, then it's fine... but then, wouldn't the = overloading work, just done in the MyInt class?<BR><BR>Maybe I got something backwards.  

Ahh, I think I finally figured out what you were asking.<BR><BR>Constructors, destructors and assignment (pure and op=) must be member functions.<BR><BR>You certainly should be able to do this:<BR><BR><pre class="ip-ubbcode-code-pre"> class A { public: A() {...} A(int a) {...} A(const A&a) {...} operator=(int a) {...} operator=(const A& a) {...} }; int main() { A a1; A a2 = a1; A a3(a1); A a4; a4 = a1; A a5(1); A a6; a6 = 10; } </pre><BR><BR>However, you cannot do this :<BR><BR><pre class="ip-ubbcode-code-pre"> class B { } operator = (B& lhs, int rhs) {...} operator += (B& lhs, const B& rhs) {...} //etc </pre>  

Tristram got what I was asking. What I meant was that I can't do<pre class="ip-ubbcode-code-pre">any_old_int = MyInstance;</pre> by creating a free function overloaded operator signed thus:<pre class="ip-ubbcode-code-pre">int operator=(int lhs, const MyClass& rhs);</pre><BR>I got that backwards in my first post.<BR><BR>Hyper165's solution is good, though; it lets me do <pre class="ip-ubbcode-code-pre">any_old_int = (int) MyInstance;</pre><BR>That's really just as good.<BR><BR>I still wonder why operator= can't be a free function, and has to be a member method.  

Not sure why, exactly, but probably because it modifes the lvalue in place, not replacing it with a copy.<BR><BR>I am pretty sure that Koenig's <I>Inside the C++ Object Model</I> talks about this, but I don't have my copy atm.  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by .mobius:<BR>Tristram got what I was asking. What I meant was that I can't do<pre class="ip-ubbcode-code-pre">any_old_int = MyInstance;</pre> by creating a free function overloaded operator signed thus:<pre class="ip-ubbcode-code-pre">int operator=(int lhs, const MyClass& rhs);</pre><BR>I got that backwards in my first post.<BR><HR></BLOCKQUOTE><BR><BR>Wouldn't the signature need to be something more like<pre class="ip-ubbcode-code-pre"> int operator=(int &lhs, const MyClass& rhs); </pre><BR><BR>Since the = operator returns the value it assigned to the lhs, but also directly does the assigning, and thus needs a reference to the lvalue so it can be rewritten?  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by QuantumET:<BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by .mobius:<BR>Tristram got what I was asking. What I meant was that I can't do<pre class="ip-ubbcode-code-pre">any_old_int = MyInstance;</pre> by creating a free function overloaded operator signed thus:<pre class="ip-ubbcode-code-pre">int operator=(int lhs, const MyClass& rhs);</pre><BR>I got that backwards in my first post.<BR><HR></BLOCKQUOTE><BR><BR>Wouldn't the signature need to be something more like<pre class="ip-ubbcode-code-pre"> int operator=(int &lhs, const MyClass& rhs); </pre><BR><BR>Since the = operator returns the value it assigned to the lhs, but also directly does the assigning, and thus needs a reference to the lvalue so it can be rewritten?<HR></BLOCKQUOTE><BR><BR>Mmm. Yes, you're right. However, the entire external operator is disallowed completely, so it's sort of moot.  

<blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by .mobius:<br><blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by QuantumET:<br><blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by .mobius:<br>Tristram got what I was asking. What I meant was that I can't do<pre class="ip-ubbcode-code-pre">any_old_int = MyInstance;</pre> by creating a free function overloaded operator signed thus:<pre class="ip-ubbcode-code-pre">int operator=(int lhs, const MyClass& rhs);</pre> <br>I got that backwards in my first post.<br><hr> </blockquote> <br><br>Wouldn't the signature need to be something more like<pre class="ip-ubbcode-code-pre"> int operator=(int &lhs, const MyClass& rhs); </pre> <br><br>Since the = operator returns the value it assigned to the lhs, but also directly does the assigning, and thus needs a reference to the lvalue so it can be rewritten?<hr> </blockquote> <br><br>Mmm. Yes, you're right. However, the entire external operator is disallowed completely, so it's sort of moot.<hr> </blockquote> <br><br>Well, yes, but that doesn't mean your imaginary declarations shouldn't be correct. -- View image here: http://arstechnica.infopop.net/infopop/emoticons/icon_wink.gif --<br><br>Perhaps there's a performance issue... if you could overload that way, every assignment into a primitive might require additional overhead to check for overloading. But you'd think this would be the case elsewhere as well.<br><br>Well, I'm sure someone knows, but it's not me.  

My interpretation.<BR><BR>If a function modifies the internal data elements of a class instance then it should ideally be a member of that class because this is a basic principle of object-oriented programming. You should not alter the content of objects through external functions but instead declare all mutator functions as members of that class (encapsulate and modularize). I know that it is possible to declare classes to be all public (struct) but doing this is not in keeping with the "ideal" of OO programming...<BR><BR>According to this principle then, an assignment operator is clearly a function that modifies the data elements of a class, so something like this should not be allowed (and is is not):<BR><BR>struct A {<BR> int i;<BR>};<BR><BR>struct B {<BR> int i;<BR>};<BR><BR>A& operator=(A& lhs, const B& rhs) {<BR> lhs.i = rhs.i;<BR> return lhs;<BR>}<BR><BR>By extension, if you agree that you should not be defining external assignment operators outside their class definition, then in order to remain consistent you probably should not be doing this for the basic data types either.<BR><BR>I know that it could be "possible" to allow programmers to write this since structs leave all data accessible to external functions. But then there are many other features that could also be added to C++, features that could be convenient and neat to have. Still, since there has to be an end to what you include in the language, and to what you decide to permit or to forbid, this particular ability may simply be a case of something that was not truly needed and/or not widely requested before the stardard got established. It's not like it prevents programmers from doing what they need to do since it is still possible to write a named function called "assign" that does exactly what is needed, but with less syntactic sugar.<BR><BR>qWake  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by qWake:<BR>My interpretation.<BR><BR>If a function modifies the internal data elements of a class instance then it should ideally be a member of that class because this is a basic principle of object-oriented programming. You should not alter the content of objects through external functions but instead declare all mutator functions as members of that class (encapsulate and modularize). I know that it is possible to declare classes to be all public (struct) but doing this is not in keeping with the "ideal" of OO programming...<BR><BR>According to this principle then, an assignment operator is clearly a function that modifies the data elements of a class, so something like this should not be allowed (and is is not):<BR><BR>struct A {<BR> int i;<BR>};<BR><BR>struct B {<BR> int i;<BR>};<BR><BR>A& operator=(A& lhs, const B& rhs) {<BR> lhs.i = rhs.i;<BR> return lhs;<BR>}<BR><BR>By extension, if you agree that you should not be defining external assignment operators outside their class definition, then in order to remain consistent you probably should not be doing this for the basic data types either.<BR><BR>I know that it could be "possible" to allow programmers to write this since structs leave all data accessible to external functions. But then there are many other features that could also be added to C++, features that could be convenient and neat to have. Still, since there has to be an end to what you include in the language, and to what you decide to permit or to forbid, this particular ability may simply be a case of something that was not truly needed and/or not widely requested before the stardard got established. It's not like it prevents programmers from doing what they need to do since it is still possible to write a named function called "assign" that does exactly what is needed, but with less syntactic sugar.<BR><BR>qWake<HR></BLOCKQUOTE><BR><BR>In your example, it would make sense to put the operator= (A& lhs, const B& rhs) into the class A as a public method, since it modifies the internal data of the instance of A.<BR><BR>What I'm having a problem with is if you want to assign from a class to a primitive. The assignment operator that affects the instance of the class still can go in the class, but the assignment operator that affects the primitive can only go into the global namespace.<BR><BR>For whatever reason, one can not do this as one can with most operators (arithmetic operators, for example, can be made global so that if I want to write MyComplex operator- (const float& lhs, const MyComplex& rhs), I can).<BR><BR>However, there's another way to look at it: we explicitly cast the class to the primitive type, and then we overload the casting operation to do what we want, so our actual expression ends up looking like some_int = (int) MyClass.<BR><BR>Using a function called assign() removes the point of operator overloading. In my particular case, that's important, because I'm making a "replacement" unsigned int that internally keeps track of more information. But when I template over a class, I'd like to be able to switch out MyClass for char and have it still work. I can't do that with assign, because some_char.assign() doesn't work.<BR><BR>I don't profess to have enough C++ experience to know why explicit casting is so preferable (or not impossible) compared to overloading the assigment operator globally as to enforce it in the language spec, but I'm satisfied that the expression I'm trying to write is still possible via explicit casts.<BR><BR>Sidenote: I think you're oversimplifying the OO encapsulation issue. If we believed that external functions should never ever be able to access internal data, then why would we have the friend keyword? Befriending a function <I>increases</I> encapsulation, beccause it allows us to selectively grant access to internal data to certain functions where it makes sense to make them global, but keep the world at large away from our internal data.<BR><BR>I'm not a big fan of OO in general, but the more I think about it, the more I think this whole issue, and my project in particular, makes sense. I'm basically writing a set of tools to do image enhancement, and I'm making an object Pixel that can internally hold some sort of high-resolution data, but be accessed in a more standard form (integer representing grayscale data, etc).<BR><BR><BR>(.mobius)  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>arithmetic operators, for example, can be made global so that if I want to write MyComplex operator- (const float& lhs, const MyComplex& rhs), I can<HR></BLOCKQUOTE><BR><BR>If you are looking for a rationale for this difference, I can suggest this: an external "operator=" function would still need to modify the content of its first parameter as well as return a reference to it. An "operator-" simply returns a newly-constructed instance so it does not require internal access to its first parameter. This difference may be significant enough to warrant disallowing the first while allowing the second. I would have to ask Mr. Stroustrup for specifics...<BR><BR><BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>some_int = (int) MyClass.<HR></BLOCKQUOTE><BR><BR>This looks like the most reasonable approach. If you wanted the language to let you do the same thing without using the cast operator then you would also run into error situations with unrelated classes, like this:<BR><BR>some_int = MyString;<BR><BR>Preventing this would require a C++ compiler to examine the content of the MyString class and see that no "int" cast operator is defined for MyString. I suppose it could be done; more work for the compiler, less for the programmer... It's not the case though, so the explicit cast appears to be the only option. You probably won't find it too annoying considering that you already need to use a cast when assigning a double to an int if you want to prevent warning messages.<BR><BR><BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Befriending a function increases encapsulation<HR></BLOCKQUOTE><BR><BR>This may be an academic consideration where the theoretical principles of OO and the realities of industrial programming are not necessarily reconcilable. An important fact is that C++ is not a strict OO language, it only offers constructs that support OO programming. It also offer a lot of constructs that go against it, and that even against basic structured programming. It still has a "goto" for heaven's sake.<BR><BR>So I would not claim that the 'friend' feature, convenient and expedient as it may be in many situations, increases encapsulation. What it does is relinquish control of the class content to an external function or an external class, and in doing this you do not maintains the "capsule" intact. Don't get me wrong, I am not arguing against using friend functions, and I make use of them whenever convenient. But when I do this I realize that my class is no longer fully encapsulated since any change I make to it must now take into consideration whatever may be happening in a different class or a different function. No big problem from a practical point of view since you can just go there and fix whatever needs to be fixed in that other class. But still the OO principle that a class is self-contained is indeed violated when we do this.<BR><BR><BR>qWake  

Ars Praefectus

  • Jun 26, 2003

Try using this http://www.rafb.net/efnet_cpp/faq/concepts/copyctor/ <BR>as a reference.<BR><BR>Your probably running into a problem with your copy constructor (which you need to define).  

Well-known member

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>This may be an academic consideration where the theoretical principles of OO and the realities of industrial programming are not necessarily reconcilable. An important fact is that C++ is not a strict OO language, it only offers constructs that support OO programming. It also offer a lot of constructs that go against it, and that even against basic structured programming. It still has a "goto" for heaven's sake.<BR><BR>So I would not claim that the 'friend' feature, convenient and expedient as it may be in many situations, increases encapsulation. <HR></BLOCKQUOTE><BR>Not in and of itself, but it can be used to increase encapsulation.  

Smack-Fu Master, in training

  • Jun 27, 2003

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by .mobius:<BR>I'm writing a wrapped data class to allow me to easily convert between several similar datatypes. That is, I have a class MyClass which internally represents an item, but can interact with ints, floats, doubles, all in predefined ways. This is a consummate example of why operator overloading is a Good Thing, right? I can write:<BR><pre class="ip-ubbcode-code-pre"> resultInt = anInt + MyInstance; resultFloat = aFloat + MyInstance; </pre><BR>and it all works transparently to the user of the class. _But_, I can't do what I want with assignment. For example:<BR><pre class="ip-ubbcode-code-pre"> myInt = myInstance; // All good and happy and right. myInstance = myInt; // Apparently not allowed. (Why not?). </pre><BR><BR>To do the second, one would naturally define <pre class="ip-ubbcode-code-pre">operator= (int lhs, const MyClass& rhs);</pre> externally and include it as a friend of MyClass. However, it seems this is not allowed (at least, according to VC++ 6 docs; I haven't tried GCC yet).<BR><BR>Why is this not allowed? How should I handle it instead? I'm at a loss, because I can't think of any other natural way to write this, and I can't find any info online about how to make it work.<BR><BR>Thanks!<BR><BR><BR>(.mobius)<BR><BR>[This message was edited by .mobius on June 25, 2003 at 15:13.]<HR></BLOCKQUOTE><BR><BR>If you put the operator function as a protected member function, you must only pass one param because C++ uses the LHS parameter for purposes of the calling the operator function and implicitly passes "this" as the LHS param. <BR><BR><pre class="ip-ubbcode-code-pre"> int operator=( int rhs ) { return *this.some_var + rhs; //or do whatever you need to. } </pre><BR><BR>or you could declare it outside the class defination and use the friend keyword, when this happens you can declare the function either way because it doesnt need to call the function as a member of the class. So you can do this:<BR><BR><pre class="ip-ubbcode-code-pre"> friend int operator=( MyClass &lhs, int &rhs) friend int operator=( int &lhs, MyClass &rhs) </pre><BR><BR>this way you can write code like the following:<BR><BR><pre class="ip-ubbcode-code-pre"> MyClass = num; num = MyClass; </pre><BR><BR>Hope this helped.<BR><BR>[This message was edited by Arrix on June 27, 2003 at 07:30.]<BR><BR>[This message was edited by Arrix on June 27, 2003 at 07:33.]  

<blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by Arrix-- View image here: http://arstechnica.infopop.net/infopop/emoticons/icon_redface.gif --r you could declare it outside the class defination and use the friend keyword, when this happens you can declare the function either way because it doesnt need to call the function as a member of the class. So you can do this:<br><br><pre class="ip-ubbcode-code-pre"> friend int operator=( MyClass &lhs, int &rhs) friend int operator=( int &lhs, MyClass &rhs) </pre> <br><br>this way you can write code like the following:<br><br><pre class="ip-ubbcode-code-pre"> MyClass = num; num = MyClass; </pre> <br><br>Hope this helped.<br><hr> </blockquote> <br><br>See, but you can't actually do that with the assignment operator. According to VC++ docs:<br><blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr> <br>The assignment operator has some additional restrictions. It can be overloaded only as a nonstatic member function, not as a friend function. It is the only operator that cannot be inherited; a derived class cannot use a base class's assignment operator. <br><hr> </blockquote> <br><br>I'm still curious why one can't overload the assignment operator in this way. Is it due to some inherent difficulty or contradiction in the language that would develop if it were possible? Is it an attempt to force 'good style' on users of the language? Or is it some combination of the two, or something else entirely?<br><br>It's not important to me right now, since I can overload the casting operator and achieve basically the same effect, but I'm interested to head if anyone has insight into the reasoning behind this limitation.  

Seniorius Lurkius

There is so much going on at the compiler level trying to make things work out for casting different things in the most optimal way - that it is probably not worth your time pursuing. <br><br>In the end, even though what your doing looks simple - there are rules that the compiler is trying to interpret and rules get added to rules that get added to rules - until not even the people who write the compiler know for sure what will happen in certain situations.<br><br>So, in the long and short of it, if it works and your sure you've tested your code for all your cases - don't worry about that low level nasty stuff. Cause everyone will have a different answer and none will probably be the same -- View image here: http://arstechnica.infopop.net/infopop/emoticons/icon_wink.gif --  

kenada

Ars Legatus Legionis

AFAIK casts made using the cast operator are implicit unless marked otherwise (at least, this was my experience using GCC 3.0 and GCC 3.2).  

From Eckel's Thinking in C++ 2nd ED, Chapter 12 - Operator Overloading<BR><BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Behavior of operator=<BR><BR>In Integer.h and Byte.h, you saw that operator= can be only a member function. It is intimately connected to the object on the left side of the '='. If it was possible to define operator= globally, then you might attempt to redefine the built-in '=' sign:<BR><BR><BR>int operator=(int, MyType); // Global = not allowed!<BR>The compiler skirts this whole issue by forcing you to make operator= a member function.<BR><BR><BR>When you create an operator=, you must copy all of the necessary information from the right-hand object into the current object (that is, the object that operator= is being called for) to perform whatever you consider 'assignment' for your class. For simple objects, this is obvious:<BR><BR><pre class="ip-ubbcode-code-pre"> //: C12:SimpleAssignment.cpp // Simple operator=() #include <iostream> using namespace std; class Value { int a, b; float c; public: Value(int aa = 0, int bb = 0, float cc = 0.0) : a(aa), b(bb), c(cc) {} Value& operator=(const Value& rv) { a = rv.a; b = rv.b; c = rv.c; return *this; } friend ostream& operator<<(ostream& os, const Value& rv) { return os << "a = " << rv.a << ", b = " << rv.b << ", c = " << rv.c; } }; int main() { Value a, b(1, 2, 3.3); cout << "a: " << a << endl; cout << "b: " << b << endl; a = b; cout << "a after assignment: " << a << endl; } ///:~ </pre><BR>Here, the object on the left side of the = copies all the elements of the object on the right, then returns a reference to itself, which allows a more complex expression to be created.<BR><BR><BR>This example includes a common mistake. When you're assigning two objects of the same type, you should always check first for self-assignment: is the object being assigned to itself? In some cases, such as this one, it's harmless if you perform the assignment operations anyway, but if changes are made to the implementation of the class, it can make a difference, and if you don't do it as a matter of habit, you may forget and cause hard-to-find bugs.<BR><BR><HR></BLOCKQUOTE><BR><BR>Available on-line for free at: <BR><BR> http://mindview.net/Books/DownloadSites  

<blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by .mobius:<br><blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr>Originally posted by Arrix-- View image here: http://arstechnica.infopop.net/infopop/emoticons/icon_redface.gif --r you could declare it outside the class defination and use the friend keyword, when this happens you can declare the function either way because it doesnt need to call the function as a member of the class. So you can do this:<br><br><pre class="ip-ubbcode-code-pre"> friend int operator=( MyClass &lhs, int &rhs) friend int operator=( int &lhs, MyClass &rhs) </pre> <br><br>this way you can write code like the following:<br><br><pre class="ip-ubbcode-code-pre"> MyClass = num; num = MyClass; </pre> <br><br>Hope this helped.<br><hr> </blockquote> <br><br>See, but you can't actually do that with the assignment operator. According to VC++ docs:<br><blockquote class="ip-ubbcode-quote"> <font size="-1">quote:</font><hr> <br>The assignment operator has some additional restrictions. It can be overloaded only as a nonstatic member function, not as a friend function. It is the only operator that cannot be inherited; a derived class cannot use a base class's assignment operator. <br><hr> </blockquote> <br><br>I'm still curious why one can't overload the assignment operator in this way. Is it due to some inherent difficulty or contradiction in the language that would develop if it were possible? Is it an attempt to force 'good style' on users of the language? Or is it some combination of the two, or something else entirely?<br><br>It's not important to me right now, since I can overload the casting operator and achieve basically the same effect, but I'm interested to head if anyone has insight into the reasoning behind this limitation.<hr> </blockquote> <br><br>Ahh yep the assignment operator is "special" in that you can only overload it as a member function just as the VC++ docs says. I totally forgot that. -- View image here: http://arstechnica.infopop.net/OpenTopic/ws/images/judge.gif --  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>When you're assigning two objects of the same type, you should always check first for self-assignment: is the object being assigned to itself? In some cases, such as this one, it's harmless if you perform the assignment operations anyway, but if changes are made to the implementation of the class, it can make a difference, and if you don't do it as a matter of habit, you may forget and cause hard-to-find bugs.<HR></BLOCKQUOTE><BR>Hrm.<BR><BR>The correct way to do it is to perform copy construction and implement a <tt>swap()</tt> member function. This allows one's operator= to provide the strong exception guarantee (which is desirable if possible) at (with a reasonable compiler) no cost to the user.<BR><BR>If one writes one's operator= in this way then the self-assignment check isn't required; it's an <em>optimization</em>, and probably a useful one at that, but it's not required for correct program behaviour.  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>The correct way to do it is to perform copy construction and implement a swap() member function.<HR></BLOCKQUOTE><BR>Can you provide an example, particularly showing what to do with the swap() member function under this scheme?  

  • Jun 28, 2003

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by .mobius:<BR>See, but you can't actually do that with the assignment operator. According to VC++ docs:<BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR><BR>The assignment operator has some additional restrictions. It can be overloaded only as a nonstatic member function, not as a friend function. It is the only operator that cannot be inherited; a derived class cannot use a base class's assignment operator. <BR><HR></BLOCKQUOTE><BR><BR>I'm still curious why one can't overload the assignment operator in this way. Is it due to some inherent difficulty or contradiction in the language that would develop if it were possible? Is it an attempt to force 'good style' on users of the language? Or is it some combination of the two, or something else entirely?<BR><BR>It's not important to me right now, since I can overload the casting operator and achieve basically the same effect, but I'm interested to head if anyone has insight into the reasoning behind this limitation.<HR></BLOCKQUOTE><BR><BR> The reasoning is pretty simple, it's just the way objects and operators relate to each other.<BR> Take the operator+ for instance. Given the statement: obj1+obj2. obj1 and obj2 are added together and it returns (implied)obj3. The same behavior is held for generally the rest of the operators. Each of these operators can be statically defined (to exist outside the scope of the class) as they 1) are type-checked against the class for the apropriate version 2) return a new copy of the object.<BR> Then we have operator=. Which by definition always has the form 'objType = '. This forcefully binds = to the objType. Of which the = operator does not create a new object for the destination which makes logical sense. If we enabled static operator= it would not be illegal to assign a value to a undefined object.  

Ars Tribunus Militum

  • Jul 2, 2003

Overloading the casting operator is generally seen as a bad idea. I believe I first came across the rationale in one of Meyer's Effective C++ books. Basically, when you define casting operators, you run the risk that the compiler will do strange things that you might not expect (in particular with argument passing in functions, IIRC). The recommendation from him was to define a member function asInt() that performs the conversion for you, but doesn't actually change the syntax of the language.<BR><BR>The reason why you can't overload intrinsic types as LHS arguments is to make it easier on programmers to have consistent behavior of intrinsic types. Basically, if you can have operator=(int, Foo), and if you have a constructor like Foo(int), then possible behavior for code:<BR>int x = 42;<BR>could be interpreted by the compiler as:<BR>int x;<BR>operator=(x, Foo(42));<BR>which is probably not what most programmers are expecting when assigning an int to an int.<BR><BR>read Meyers for a more thorough explanation.<BR><BR>--sam  

<BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>Originally posted by qWake:<BR><BLOCKQUOTE class="ip-ubbcode-quote"><font size="-1">quote:</font><HR>The correct way to do it is to perform copy construction and implement a swap() member function.<HR></BLOCKQUOTE><BR>Can you provide an example, particularly showing what to do with the swap() member function under this scheme?<HR></BLOCKQUOTE><BR><pre class="ip-ubbcode-code-pre"> class Foo { public: void swap(Foo& rhs) // nothrow { s.swap(rhs.s); // nothrow std::swap(i, rhs.i); // nothrow c.swap(rhs.c); // nothrow } Foo& operator=(const Foo& rhs) { Foo temp(rhs); // copy construct -- may throw. But if it does, neither *this nor rhs are altered. temp.swap(*this); // nothrow } private: std::string s; int i; SomeOtherClassThatCanSwap c; }; </pre><BR><BR>If you want to perform the self-assignment test, you can. It'll tend to enhance performance a little. But it's not required. By writing the assignment operator in this way we are sure that either the assignment is successful, or the object is left unmodified. The price is some extra copying.  

C++ overloading: operators to overload as methods of a class

assignment operator outside class

Get Started With Machine Learning

Learn the fundamentals of Machine Learning with this free course. Future-proof your career by adding ML skills to your toolkit — or prepare to land a job in AI or Data Science.

Operator overloading is an interesting topic of object-oriented programming using C++. Operator overloading enables the objects of a class to be used in expressions. Some operators must be overloaded as non-static member functions of the class. This blog discusses these operators with coding examples.

Let’s dive right in!

We’ll cover:

Introduction

  • Assignment operators

Single overload

Two ways to overload, noninteger index, function call operator, operators that are preferred as members, wrapping up and next steps.

Operators are overloaded as functions in C++. They can be overloaded in various ways: as member functions only, as nonmembers only, and as both members and nonmembers. Here, we discuss the operators that can be overloaded as non-static member functions only. The first operand or the caller of these operators is always an object of the class. Other aspects related to operator overloading are discussed in Operator Overloading in C++ and Various Ways of Overloading an Operator in C++ .

The operators that can be overloaded in C++. The green subset must be overloaded as methods.

From this point onward, we’ll keep using and extending this minimal implementation of the Time class.

Lines 6–29: We define a Time class as described before the code. Additionally, it defines the subscript operator ( lines 21–28 ).

  • Line 21: This receives a string parameter to mention the specific attribute that we want to access for read/write. It accepts only "hour" , "minute" , or "second" as an argument. It throws an exception if it receives any other string.

Lines 31–43: We define the main() function that creates an object noon of type Time .

Line 36: We call the subscript operator to read the value of "hour" from noon .

Line 39: We call the subscript operator to write the value of "second" to noon .

Zero to Hero in C++

Cover

Learn in-demand tech skills in half the time

Skill Paths

Assessments

Learn to Code

Tech Interview Prep

Generative AI

Data Science

Machine Learning

GitHub Students Scholarship

Early Access Courses

For Individuals

Try for Free

Become an Author

Become an Affiliate

Earn Referral Credits

Frequently Asked Questions

Privacy Policy

Cookie Policy

Terms of Service

Business Terms of Service

Data Processing Agreement

Copyright © 2024 Educative, Inc. All rights reserved.

cppreference.com

User-defined conversion function.

Enables implicit conversion or explicit conversion from a class type to another type.

[ edit ] Syntax

Conversion function is declared like a non-static member function or member function template with no parameters, no explicit return type, and with the name of the form:

conversion-type-id is a type-id except that function and array operators [] or () are not allowed in its declarator (thus conversion to types such as pointer to array requires a type alias/typedef or an identity template: see below). Regardless of typedef, conversion-type-id cannot represent an array or a function type.

Although the return type is not allowed in the declaration of a user-defined conversion function, the decl-specifier-seq of the declaration grammar may be present and may include any specifier other than type-specifier or the keyword static , In particular, besides explicit , the specifiers inline , virtual , constexpr (since C++11) , consteval (since C++20) , and friend are also allowed (note that friend requires a qualified name: friend A :: operator B ( ) ; ).

When such member function is declared in class X, it performs conversion from X to conversion-type-id :

[ edit ] Explanation

User-defined conversion function is invoked in the second stage of the implicit conversion , which consists of zero or one converting constructor or zero or one user-defined conversion function.

If both conversion functions and converting constructors can be used to perform some user-defined conversion, the conversion functions and constructors are both considered by overload resolution in copy-initialization and reference-initialization contexts, but only the constructors are considered in direct-initialization contexts.

Conversion function to its own (possibly cv-qualified) class (or to a reference to it), to the base of its own class (or to a reference to it), and to the type void can be defined, but can not be executed as part of the conversion sequence, except, in some cases, through virtual dispatch:

It can also be called using member function call syntax:

When making an explicit call to the conversion function, conversion-type-id is greedy: it is the longest sequence of tokens that could possibly form a conversion-type-id (including attributes, if any) (since C++11) :

Conversion functions can be inherited and can be virtual , but cannot be static . A conversion function in the derived class does not hide a conversion function in the base class unless they are converting to the same type.

Conversion function can be a template member function, for example, std::auto_ptr<T>::operator auto_ptr<Y> . See member template and template argument deduction for applicable special rules.

[ edit ] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • Recent changes
  • Offline version
  • What links here
  • Related changes
  • Upload file
  • Special pages
  • Printable version
  • Permanent link
  • Page information
  • In other languages
  • This page was last modified on 9 February 2024, at 00:06.
  • This page has been accessed 906,266 times.
  • Privacy policy
  • About cppreference.com
  • Disclaimers

Powered by MediaWiki

IMAGES

  1. Assignment operator

    assignment operator outside class

  2. PPT

    assignment operator outside class

  3. PPT

    assignment operator outside class

  4. PPT

    assignment operator outside class

  5. 14

    assignment operator outside class

  6. PPT

    assignment operator outside class

VIDEO

  1. assignment 1 (C)

  2. Assignment 0

  3. Storing values with assignment operator

  4. Assignment 4 Tutorial recording

  5. 1.03 Assignment Help

  6. 12

COMMENTS

  1. Overloading Assignment operator outside of class

    1 Answer. Ignoring the code and cutting to the core of the question as stated in the comments: I just wanted to know if it was possible to operator overload the assignment operator outside the class. the answer is no. [class.copy.assign] expressly forbids it. I only have n4618 on hand at the moment, so that's what I'll quote:

  2. c++

    24. First: the two different ways are really "overload as a member" and "overload as a non-member", and the latter has two different ways to write it (as-friend-inside class definition and outside class definition). Calling them "inside class" and "outside class" is going to confuse you. Overloads for +=, +, -=, -, etc. have a special pattern:

  3. Assignment operators

    for assignments to class type objects, the right operand could be an initializer list only when the assignment is defined by a user-defined assignment operator. removed user-defined assignment constraint. CWG 1538. C++11. E1 ={E2} was equivalent to E1 = T(E2) ( T is the type of E1 ), this introduced a C-style cast. it is equivalent to E1 = T{E2}

  4. C++ Assignment Operator Overloading

    The assignment operator,"=", is the operator used for Assignment. It copies the right value into the left value. Assignment Operators are predefined to operate only on built-in Data types. Assignment operator overloading is binary operator overloading. Overloading assignment operator in C++ copies all values of one object to another object.

  5. 21.12

    Overloading the assignment operator. Overloading the copy assignment operator (operator=) is fairly straightforward, with one specific caveat that we'll get to. The copy assignment operator must be overloaded as a member function. #include <cassert> #include <iostream> class Fraction { private: int m_numerator { 0 }; int m_denominator { 1 ...

  6. operator overloading

    In those situations where copy assignment cannot benefit from resource reuse (it does not manage a heap-allocated array and does not have a (possibly transitive) member that does, such as a member std::vector or std::string), there is a popular convenient shorthand: the copy-and-swap assignment operator, which takes its parameter by value (thus working as both copy- and move-assignment ...

  7. Copy assignment operator

    5,6) Definition of a copy assignment operator outside of class definition (the class must contain a declaration (1) ). 6) The copy assignment operator is explicitly-defaulted. The copy assignment operator is called whenever selected by overload resolution, e.g. when an object appears on the left side of an assignment expression.

  8. Assignment Operators

    If self-assignment can be handled without any extra code, don't add any extra code. But do add a comment so others will know that your assignment operator gracefully handles self-assignment: Example 1a: Fred& Fred::operator= (const Fred& f) {. // This gracefully handles self assignment. *p_ = *f.p_; return *this;

  9. C++ Operator Overloading Guidelines

    C++ Operator Overloading Guidelines. One of the nice features of C++ is that you can give special meanings to operators, when they are used with user-defined classes. This is called operator overloading. You can implement C++ operator overloads by providing special member-functions on your classes that follow a particular naming convention.

  10. Assignment operators

    Explanation. copy assignment operator replaces the contents of the object a with a copy of the contents of b ( b is not modified). For class types, this is a special member function, described in copy assignment operator . move assignment operator replaces the contents of the object a with the contents of b, avoiding copying if possible ( b may ...

  11. Assignment Operators In C++

    In C++, the assignment operator can be combined into a single operator with some other operators to perform a combination of two operations in one single statement. These operators are called Compound Assignment Operators. There are 10 compound assignment operators in C++: Addition Assignment Operator ( += ) Subtraction Assignment Operator ...

  12. c++

    I has been trying to the excess which mapping operator outside an class using a friend class but no matter what I do it doesn't work. (yes I know the convent is at overload the operator ins... Multi Overflow. About; ... Overloading Assignment operator outside of class ...

  13. Scope resolution operator in C++

    A's x is 10. B's x is 20. 5) For namespace If a class having the same name exists inside two namespaces we can use the namespace name with the scope resolution operator to refer that class without any conflicts. C++. #include <bits/stdc++.h>. #include <iostream>. using namespace std;

  14. 21.9

    Why operator[] returns a reference. Let's take a closer look at how list[2] = 3 evaluates. Because the subscript operator has a higher precedence than the assignment operator, list[2] evaluates first.list[2] calls operator[], which we've defined to return a reference to list.m_list[2].Because operator[] is returning a reference, it returns the actual list.m_list[2] array element.

  15. Move assignment operator

    Triviality of eligible move assignment operators determines whether the class is a trivially copyable type. [] NoteIf both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either a prvalue such as a nameless temporary or an xvalue such as the result of std::move), and selects the copy assignment if the argument is an ...

  16. How to define an assignment operator outside my header file in C++?

    12. I have a header file which contains a class with an assignment operator. Now I want to define my function inside my .cpp file. My header file contains: Car &operator= (const Car &other); Now in my cpp file I would like to do something like: Car::Car &operator= (const Car &other) { } Unfortunately this doesn't seem to be the right syntax.

  17. [C++] Why can't I externally overload operator= with my class on the

    The assignment operator that affects the instance of the class still can go in the class, but the assignment operator that affects the primitive can only go into the global namespace.<BR><BR>For ...

  18. C++ overloading: operators to overload as methods of a class

    Some operators must be overloaded as member functions of the class. Let's discover the intriguing world of operator overloading in C++. We explore how certain operators, like the assignment, subscript, and function call operators, must be overloaded as member functions, making them integral to class objects. We'll also discuss the flexibility of the function call operator and its varied uses.

  19. User-defined conversion function

    A conversion function in the derived class does not hide a conversion function in the base class unless they are converting to the same type. Conversion function can be a template member function, for example, std::auto_ptr<T>::operator auto_ptr<Y>. See member template and template argument deduction for applicable special rules. Defect reports

  20. overloading assignment operators when the class is a child

    I implement the operator= functionality assigning/constructing color in the Base class operator if you want to call the Base operator= from the Derived class use: Base::operator=(rhs) in the Derived class operator=() implementation. The signature you've proposed for Derived operator= isn't valid C++ as far as I know.