C++ Operator Overloading and Non-Overloadable Operators
This article provides an overview of operator overloading in C++, explaining how it allows developers to redefine how standard operators interact with user-defined types. It covers the core concept and purpose of operator overloading, followed by an explicit breakdown of the specific C++ operators that the language standard strictly prohibits from being overloaded, along with the technical reasons behind these restrictions.
What Is Operator Overloading?
Operator overloading is a compile-time polymorphism feature in C++
that allows standard operators (such as +, -,
*, ==, and <<) to have
user-defined meanings when applied to custom data types like classes and
structs.
In standard C++, operators are predefined for primitive types (e.g.,
adding two integers using a + b). Through operator
overloading, you can define a special member function or a non-member
friend function using the operator keyword so that your
custom objects can be manipulated using the same intuitive syntax. For
example, rather than calling a method like
vector1.add(vector2), you can write
vector1 + vector2.
Operators That Cannot Be Overloaded
While C++ allows the majority of its operators to be overloaded, a small set cannot be modified. The C++ standard explicitly forbids overloading the following operators:
1. Scope Resolution Operator
(::)
The scope resolution operator is resolved entirely at compile time to specify namespace, class, or enumeration scopes. Overloading it would create severe syntactic ambiguity, making it impossible for the compiler to distinguish between a namespace lookup and an overloaded operation.
2. Member Access Operator
(.)
The direct member access (dot) operator is fundamental to accessing
members of an object. Allowing this operator to be overloaded would
compromise member lookup logic and obscure how class members are
reached. Note that while . cannot be overloaded, the
pointer-to-member arrow operator (->) can be.
3. Member Pointer Selector
Operator (.*)
Similar to the dot operator, the pointer-to-member dereference
operator (.*) is bound to the core mechanics of accessing
member functions or variables via pointers within an instance.
Overloading it would disrupt object-model guarantees. The indirect
version (->*), however, can be overloaded.
4. Ternary Conditional Operator
(?:)
The ternary conditional operator evaluates an expression based on a
condition: condition ? true_expr : false_expr. Crucially,
it guarantees short-circuit evaluation—only one of the two expressions
is evaluated. In C++, overloaded operators act as function calls where
all arguments must be evaluated before the function executes.
Overloading ?: would break this short-circuiting
guarantee.
5. Object Size Operator
(sizeof)
The sizeof operator returns the memory size of a type or
object in bytes. It is evaluated at compile time and is vital for
low-level memory allocation, array sizing, and type layout. Modifying
its behavior would fundamentally break memory management.
6. Type Identification
Operator (typeid)
Part of Run-Time Type Information (RTTI), typeid queries
the runtime type of an expression. Because the integrity of the type
system relies on typeid providing accurate metadata, C++
prohibits user-defined overrides.
7. Alignment Operator
(alignof)
Introduced in modern C++, alignof queries the alignment
requirement of a type at compile time. Like sizeof, it is
an intrinsic compiler query that cannot be altered.
8. Casting Operators
Explicit casting operators—such as static_cast,
dynamic_cast, const_cast, and
reinterpret_cast—are intrinsic type-conversion keywords
rather than traditional overloadable operators.