Copy assignment operator

A copy assignment operator of class T is a non-template non-static member function with the name operator = that takes exactly one parameter of type T , T & , const T & , volatile T & , or const volatile T & . A type with a public copy assignment operator is CopyAssignable .

[ edit ] Syntax

[ edit ] explanation.

  • Typical declaration of a copy assignment operator when copy-and-swap idiom can be used
  • Typical declaration of a copy assignment operator when copy-and-swap idiom cannot be used
  • Forcing a copy assignment operator to be generated by the compiler
  • Avoiding implicit copy assignment

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.

[ edit ] Implicitly-declared copy assignment operator

If no user-defined copy assignment operators are provided for a class type ( struct , class , or union ), the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T & T :: operator = ( const T & ) if all of the following is true:

  • each direct base B of T has a copy assignment operator whose parameters are B or const B& or const volatile B &
  • each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M& or const volatile M &

Otherwise the implicitly-declared copy assignment operator is declared as T & T :: operator = ( T & ) . (Note that due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument)

A class can have multiple copy assignment operators, e.g. both T & T :: operator = ( const T & ) and T & T :: operator = ( T ) . If some user-defined copy assignment operators are present, the user may still force the generation of the implicitly declared copy assignment operator with the keyword default .

Because the copy assignment operator is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.

[ edit ] Deleted implicitly-declared copy assignment operator

The implicitly-declared or defaulted copy assignment operator for class T is defined as deleted in any of the following is true:

  • T has a non-static data member that is const
  • T has a non-static data member of a reference type.
  • T has a non-static data member that cannot be copy-assigned (has deleted, inaccessible, or ambiguous copy assignment operator)
  • T has direct or virtual base class that cannot be copy-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
  • T has a user-declared move constructor
  • T has a user-declared move assignment operator

[ edit ] Trivial copy assignment operator

The implicitly-declared copy assignment operator for class T is trivial if all of the following is true:

  • T has no virtual member functions
  • T has no virtual base classes
  • The copy assignment operator selected for every direct base of T is trivial
  • The copy assignment operator selected for every non-static class type (or array of class type) memeber of T is trivial

A trivial copy assignment operator makes a copy of the object representation as if by std:: memmove . All data types compatible with the C language (POD types) are trivially copy-assignable.

[ edit ] Implicitly-defined copy assignment operator

If the implicitly-declared copy assignment operator is not deleted or trivial, it is defined (that is, a function body is generated and compiled) by the compiler. For union types, the implicitly-defined copy assignment copies the object representation (as by std:: memmove ). For non-union class types ( class and struct ), the operator performs member-wise copy assignment of the object's bases and non-static members, in their initialization order, using, using built-in assignment for the scalars and copy assignment operator for class types.

The generation of the implicitly-defined copy assignment operator is deprecated (since C++11) if T has a user-declared destructor or user-declared copy constructor.

[ edit ] Notes

If both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either prvalue such as a nameless temporary or xvalue such as the result of std:: move ), and selects the copy assignment if the argument is lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.

[ edit ] Copy and swap

Copy assignment operator can be expressed in terms of copy constructor, destructor, and the swap() member function, if one is provided:

T & T :: operator = ( T arg ) { // copy/move constructor is called to construct arg     swap ( arg ) ;     // resources exchanged between *this and arg     return * this ; }   // destructor is called to release the resources formerly held by *this

For non-throwing swap(), this form provides strong exception guarantee . For rvalue arguments, this form automatically invokes the move constructor, and is sometimes referred to as "unifying assignment operator" (as in, both copy and move).

[ edit ] Example

Copy assignment operator

A copy assignment operator of class T is a non-template non-static member function with the name operator= that takes exactly one parameter (that isn't an explicit object parameter ) of type T , T& , const T& , volatile T& , or const volatile T& . For a type to be CopyAssignable , it must have a public copy assignment operator.

Explanation

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.

Implicitly-declared copy assignment operator

If no user-defined copy assignment operators are provided for a class type ( struct , class , or union ), the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T& T::operator=(const T&) if all of the following is true:

  • each direct base B of T has a copy assignment operator whose parameters are B or const B& or const volatile B& ;
  • each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M& or const volatile M& .

Otherwise the implicitly-declared copy assignment operator is declared as T& T::operator=(T&) . (Note that due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument.).

A class can have multiple copy assignment operators, e.g. both T& T::operator=(T&) and T& T::operator=(T) . If some user-defined copy assignment operators are present, the user may still force the generation of the implicitly declared copy assignment operator with the keyword default . (since C++11) .

The implicitly-declared (or defaulted on its first declaration) copy assignment operator has an exception specification as described in dynamic exception specification (until C++17) noexcept specification (since C++17) .

Because the copy assignment operator is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.

Deleted implicitly-declared copy assignment operator

An implicitly-declared copy assignment operator for class T is defined as deleted if any of the following is true:

  • T has a user-declared move constructor;
  • T has a user-declared move assignment operator.

Otherwise, it is defined as defaulted.

A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:

  • T has a non-static data member of a const-qualified non-class type (or array thereof);
  • T has a non-static data member of a reference type;
  • T has a non-static data member or a direct base class that cannot be copy-assigned (overload resolution for the copy assignment fails, or selects a deleted or inaccessible function);
  • T is a union-like class , and has a variant member whose corresponding assignment operator is non-trivial.

Trivial copy assignment operator

The copy assignment operator for class T is trivial if all of the following is true:

  • it is not user-provided (meaning, it is implicitly-defined or defaulted);
  • T has no virtual member functions;
  • T has no virtual base classes;
  • the copy assignment operator selected for every direct base of T is trivial;
  • the copy assignment operator selected for every non-static class type (or array of class type) member of T is trivial.

A trivial copy assignment operator makes a copy of the object representation as if by std::memmove . All data types compatible with the C language (POD types) are trivially copy-assignable.

Eligible copy assignment operator

Triviality of eligible copy assignment operators determines whether the class is a trivially copyable type .

Implicitly-defined copy assignment operator

If the implicitly-declared copy assignment operator is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used or needed for constant evaluation (since C++14) . For union types, the implicitly-defined copy assignment copies the object representation (as by std::memmove ). For non-union class types ( class and struct ), the operator performs member-wise copy assignment of the object's bases and non-static members, in their initialization order, using built-in assignment for the scalars and copy assignment operator for class types.

If 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 lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.

It is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined copy assignment operator (same applies to move assignment ).

See assignment operator overloading for additional detail on the expected behavior of a user-defined copy-assignment operator.

Defect reports

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

  • converting constructor
  • copy constructor
  • copy elision
  • default constructor
  • aggregate initialization
  • constant initialization
  • copy initialization
  • default initialization
  • direct initialization
  • initializer list
  • list initialization
  • reference initialization
  • value initialization
  • zero initialization
  • move assignment
  • move constructor

© cppreference.com Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. https://en.cppreference.com/w/cpp/language/as_operator

In closing, I will defer again to the experts. In this case, Herb Sutter:

Mead's Guide to Modern C++ (An Introduction)

  • Inheriting Constructors ( N2540)
g++ -std=c++11 foo.cpp clang++ -std=c++14 foo.cpp g++ -std=c++17 foo.cpp cl /std:c++17 foo.cpp
#include <algorithm> #include <array> #include <bitset> #include <cmath> #include <deque> #include <initializer_list> #include <iostream> #include <list> #include <set> #include <string> #include <tuple> #include <typeinfo> #include <vector>
using namespace std;

Right Angle Brackets ( N1757 )

vector<list< int >> vl; // Error in C++03 because >> is the right shift operator! vector<list< int > > vl; // This was the "fix" in C++03
error: spurious '>>', use '>' to terminate a template argument list std::vector<std::list>> vl;
error: a space is required between consecutive right angle brackets (use '> >') std::vector<std::list>> vl; ^~ > >

Deprecated register keyword ( P0001R1 )

register int i = 10; // deprecated in C++11
warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register] register int i = 10; ^~~~~~~~~ 1 warning generated.
error: ISO C++17 does not allow 'register' storage class specifier [-Wregister] register int i = 10; ^~~~~~~~~ 1 error generated.
int register = 10; // use register as a variable name
error: expected unqualified-id int register = 10; ^

Binary Literals ( N3472 )

// All values are equivalent, decimal 17 int i1 = 17; // decimal int i2 = 021; // octal (leading zero) int i3 = 0x11; // hexadecimal (leading 0x or OX) int i4 = 0b10001; // C++14 binary (leading Ob or OB) // All lines output 17 (decimal) cout << "i1 is " << i1 << endl; cout << "i2 is " << i2 << endl; cout << "i3 is " << i3 << endl; cout << "i4 is " << i4 << endl; // Overriding the default decimal base cout << oct << showbase << i1 << endl; // 021 cout << hex << showbase << i1 << endl; // 0x11 // Unfortunately, there currently is no built-in way to print in binary cout << bin << showbase << i1 << endl; // The bin manipulator doesn't exist!!
cout << bitset<5>(i1) << endl; // output: 10001 cout << bitset<8>(i1) << endl; // output: 00010001 cout << bitset<16>(i1) << endl; // output: 0000000000010001 cout << bitset<32>(i1) << endl; // output: 00000000000000000000000000010001 cout << bitset<47>(i1) << endl; // output: 00000000000000000000000000000000000000000010001
#include <bitset>

Single-Quote As a Digit Separator ( N3781 )

long long a = 10000000000; // Hold on, let me count the zeros...
long long b = 10,000,000,000; // Easy! 10 billion
long long b = 10'000'000'000; // Still easy to read
long long b; b = 10000000000; b = 10'000'000'000; b = 100'0000'0000; b = 1'0000'000000; b = 100'00'00'00'00; b = 1'0'0'0'0'0'0'0'0'0'0;
int i1 = 1'234'789; // decimal int i2 = 01'234'567; // octal int i3 = 0x1'abc'def; // hexadecimal int i4 = 0b1'000'101; // binary
float f = 3.14'999'999F; // float double d1 = 1'234.7'200'009; // double double d2 = 1.234'567e+123; // double (e-notation) double d3 = 1.2'3'4'5'6'7e+1'2'3; // crazy!!
  • This feature was added in C++14, so the compiler needs to be more compliant than just C++11.
  • Although it looks like you can put quotes anywhere, there are rules to follow.
  • You can't have two adjacent quotes.
  • You can't have a quote next to a decimal point.
  • You can't have a quote next to the e .
  • You can't have a quote next to the x or b in hex/binary representations.
  • In other words, the single quotes must be placed between two digits.
  • IMHO, this is one of those seemingly-trivial things that should have been part of the language decades ago.
  • To see how different cultures use different delimiters, check this out.

Explict Conversion Operators ( N2437 )

class Foo3 { public : // Conversion constructor Foo3( int x) : x_(x) { } private : int x_; };
void fn1( const Foo3& f) { // Do something with f } Foo3 f(42); // Construct a Foo3 object from an integer (conversion constructor) f = 43; // The conversion constructor is called implicitly fn1(45); // The conversion constructor is called implicitly
// Explicit conversion constructor explicit Foo3( int x) : x_(x) { }
error: no viable overloaded '=' f = 43; // The conversion constructor is called implicitly ~ ^ ~~ note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'int' to 'const Foo3' for 1st argument class Foo3 ^ error: no matching function for call to 'fn1' fn1(43); // The conversion constructor is called implicitly ^~~ note: candidate function not viable: no known conversion from 'int' to 'const Foo3' for 1st argument void fn1(const Foo3& f) ^ 2 errors generated.
f = Foo3(43); // OK, explicit cast fn1(Foo3(43)); // OK, explicit cast
Foo3 f(42); // Construct a Foo3 object from an integer int i = f; // Error: Can't assign a Foo3 to an integer int j = ( int )f; // Error: Still can't do it (compiler doesn't know how) int k = int (f); // Nope int n = static_cast < int >(f); // Nope
class Foo3 { public : // Conversion constructor explicit Foo3( int x) : x_(x) { } // Conversion operator operator int () const { return x_; } private : int x_; };
// Conversion operator explicit operator int () const { return x_; }
int i = f; // Error: Can't implicitly convert a Foo3 to integer
int j = ( int )f; // OK, explicit C-style cast int k = int (f); // OK, explicit C++-style cast int n = static_cast < int >(f); // OK, explicit C++ named cast
void prints( const string& str) { cout << str << endl; } Foo3 f(42); // Construct a Foo3 object from an integer prints(f); // Error: No conversion from Foo3 to string
// You can mark this explicit , if necessary operator std::string() const { return std::to_string(x_); // to_string is new in C++11 }
Foo3 f(42); // Construct a Foo3 object from an integer prints(f); // OK, now prints 42
  • Having the compiler automatically and silently convert between types is a double-edged sword.
  • It makes it convenient for the programmer.
  • However, it can perform unintended conversions.

Uniform Initialization ( uniform-init a.k.a. Brace Initialization)

This is actually part of Initializer Lists (N2672) described below, but I wanted to demonstrate the simplest uses before the more complicated ones.

int a = 1; // Copy initialization ("assignment" operator) int b(3); // Direct initialization (parentheses)
// Use "assignment" operator and braces to initialize the arrays int array1[] = {1, 2, 3}; const char *array2[] = { "one" , "two" , "three" };
struct Point { double x; double y; }; // Use "assignment" operator and braces to initialize the struct Point p = {1.0, 2.0};
class Foo { public : Foo( int v1, int v2) : a_(v1) // This is initialization (member initializer list) { b_ = v2; // This is assignment } private : int a_; int b_; }; Foo f(1, 2); // a_ will be initialized by the constructor
// Simple initialization double d1 = 3.14; // C++98 (copy initialization) double d2 = {3.14}; // C++98 (brace initialization, was almost never used) double d3(3.14); // C++98 (direct initialization) double d4{3.14}; // C++11 (brace initialization)
int i1 = 3.14; // Clang warns, g++ is silent (even with -Wall, need -Wconversion) int i2(3.14); // Clang warns, g++ is silent (even with -Wall, need -Wconversion) int i3{3.14}; // Clang error, g++ error int i4 = {3.14}; // Clang error, g++ error (new behavior)
int i5 = 1; // OK double d5 = 2; // OK double d6{1}; // OK, literal is known at compile-time double d7{i5}; // Clang error, g++ warning, int i6{d5}; // Clang error, g++ warning,
int ii1; // Undefined (assume local inside of a function) int ii2(); // Function prototype! a.k.a " the most vexing parse " (Clang warns) int ii3{}; // 0 (New) int ii4 = {}; // 0 int ii5 = int (); // 0 int ii6 = int {}; // 0 (New)
"Prefer the {}-initializer syntax for declarations with a named type."
struct Point { double x; double y; }; Point p = {1.0, 2.0}; // Use "assignment" operator and braces to initialize the struct
Point p {1.0, 2.0}; // Use braces to initialize the struct
Point p {}; // x and y are both 0.0
Point p; // x and y are both undefined
  • No user-defined non-default constructor
  • No private or protected non-static data members
  • No virtual functions
  • No virtual, private, or protected base classes
struct S1 { public : void print() { cout << "s is " << s << ", i is " << i << ", d is " << d << endl; } // All are public string s; int i; double d; }; S1 s1; // Undefined values for built-in types int and double (string is initialized to "") S1 s2 = { "" , 10, 3.14}; // Initialize all members (public) S1 s3 = {}; // All members default-initialized {"", 0, 0.0} S1 s4 { "" , 10, 3.14}; // (C++11) Using uniform-initialization S1 s5 {}; // (C++11) Using uniform-initialization {"", 0, 0.0} s1.print(); // s is , i is 166494208, d is 3.11218e-317 s2.print(); // s is , i is 10, d is 3.14 s3.print(); // s is , i is 0, d is 0 s4.print(); // s is , i is 10, d is 3.14 s5.print(); // s is , i is 0, d is 0
struct S2 { public : void print() { cout << "s is " << s << ", i is " << i << ", d is " << d << endl; } private : string s; int i; double d; }; S2 s6; // Undefined values for built-in types int and double S2 s8 = {}; // All members default-initialized {"", 0, 0.0} S2 s10 {}; // (C++11) all members default-initialized {"", 0, 0.0} S2 s11 = { "" , 10, 3.14}; // This is still illegal (private) s6.print(); // s is , i is 166494208, d is 3.11218e-317 s8.print(); // s is , i is 0, d is 0 s10.print(); // s is , i is 0, d is 0
Technically, S2 is not a POD because it has private data. However, now using uniform-initialization, you can default-initialize them without a constructor. This may seem like a small advantage, and it probably is. The one other thing that is potentially more useful is that, since C++17, PODs can inherit from certain base classes (not shown here). This was not possible before C++11.
  • Variable Initialization The difference between default initialization, direct initialization, copy initialization, and list initialization. Also, the difference between using ( ) and using { } for initialization.
  • Uniform initialization syntax and semantics at Stroustrup's site.
  • Extension to aggregate initialization This was added to C++17.

Initializer Lists ( N2672 )

Member Array Initialization

class Foo2 { public : Foo2() { // C++98/03 can't initialize a member array. Must use a loop and "assign" values. for ( int i = 0; i < 3; i++) array_[i] = i + 1; } private : int array_[3]; // How to initialize? };
class Foo2 { public : Foo2() : array_{1, 2, 3} // member array initialization (MUST use braces) { // empty body } private : int array_[3]; };
Foo2() : array_( {1, 2, 3} ) // Incorrect
class Foo2 { public : Foo2() { // empty body } private : int array_[3] = {1, 2, 3}; // member array initialization (equal sign is optional) };
error: array bound cannot be deduced from an in-class initializer int array_[] = {1, 2, 3}; // member array initialization (equal sign is optional) ^
error: too many initializers for 'int [0]' int array_[] = {1, 2, 3}; // member array initialization (equal sign is optional) ^
// size and capacity is 0 vector< int > v0; // Add elements v0.push_back(1); v0.push_back(2); v0.push_back(3); v0.push_back(4); v0.push_back(5); // Size is 5, capacity is implementation-dependent (it's 8 with clang) cout << "size: " << v0.size() << ", capacity: " << v0.capacity() << endl; // Initialize vector with initializer list syntax vector< int > v1{1, 2, 3, 4, 5}; // Size and capacity are both 5 cout << "size: " << v1.size() << ", capacity: " << v1.capacity() << endl;
list< int > list1 {5, 4, 3, 2, 1}; deque< int > deque1 {3, 4, 5, 1, 2}; set< int > set1 {5, 4, 3, 2, 1};
5 4 3 2 1 3 4 5 1 2 1 2 3 4 5
// Function taking a vector void fn1( const std::vector< int >& v) { cout << "vector: " ; print5(v); } // Function taking an initializer_list void fn2( const std::initializer_list< int >& lst) { cout << "init list: " ; print5(lst); }
fn1({1, 2, 3}); // conversion constructor for vector fn1(vector< int >{1, 2, 3, 4}); // pass vector (temporary) fn2({1, 2, 3, 4, 5}); // pass initializer_list fn1(1, 2, 3, 4, 5); // Error fn2(1, 2, 3, 4, 5); // Error
vector: 1 2 3 vector: 1 2 3 4 init list: 1 2 3 4 5
cpp14.cpp:357:3: error: no matching function for call to 'fn1' fn1(1, 2, 3); ^~~ cpp14.cpp:244:6: note: candidate function not viable: requires single argument 'v', but 3 arguments were provided void fn1(const std::vector & v) ^ cpp14.cpp:358:3: error: no matching function for call to 'fn2' fn2(1, 2, 3); ^~~ cpp14.cpp:250:6: note: candidate function not viable: requires single argument 'lst', but 3 arguments were provided void fn2(const std::initializer_list & lst) ^
  • Since there is a (conversion) constructor for the vector class that takes an initializer_list , the first call will work by implicitly constructing a vector.
  • The second call explicitly constructs an anonymous vector object from the initializer_list and then passes that.
  • The third call exactly matches the function that takes an initializer_list.
  • You could have overloaded fn1 instead of creating a second function with a different name. Beware, of course, that overloading can lead to ambiguities.
#include <initializer_list>
Vector( const std::initializer_list< int >& list);
Vector::Vector( const std::initializer_list< int >& list) : array_(0), size_(0), capacity_(0) { // Get the number of elements in the intializer list size_t size = list.size(); // The initializer list could be empty if (size) { // Just grow the internal array once (optimization) grow(size); // Iterate over the initializer list, pushing each element to the back std::initializer_list< int >::const_iterator it = list.begin(); while (it != list.end()) push_back(*it++); } }
Vector v {1, 2, 3, 4, 5}; Print(v);
1 2 3 4 5 (size=5, capacity=5, allocs=1)
  • Variable Initialization The difference between default initialization, direct initialization, copy initialization, and list initialization. Also, the difference between using ( ) and using { } for initialization. If you are going to use braces and initializer lists, this is a must read.
  • Initializer lists at Stroustrup's site.

Range-Based For Loops ( N2930 )

template < typename InputIt, typename Op> Op for_each (InputIt first, InputIt last, Op op);
// Initialize int array (20 bytes) int a[] = {1, 2, 3, 4, 5}; // Print out each element: 1 2 3 4 5 int size = sizeof (a) / sizeof (*a); for ( int i = 0; i < size; i++) cout << a[i] << " " ; cout << endl;
// Using a range-based for: 1 2 3 4 5 for ( int i : a) cout << i << " " ; cout << endl;
// Multiply each element by 10 (reference) for ( int & i : a) i *= 10; // Display: 10 20 30 40 50 for ( int i : a) cout << i << " " ; cout << endl;
// Assume some array of large objects const BigObject BigObjectArray[] = {. . .}; // Don't want to copy large objects, but don't want to modify either for ( const BigObject& b : BigObjectArray) ProcessBigObject(b);
Although I'm showing you how to use the range-based for with static arrays (for simplicity now), you will probably rarely do this because of the limitations of static arrays. Range-based for was primarily developed for containers (specifically STL containers). Also, static arrays are now used rarely because of the far superior std::array container (see below ) that has been added to the STL.
list< int > lst {1, 2, 3, 4, 5}; // Using C++11 initializer lists // Using C++98/03 iterators with for loop: 1 2 3 4 5 for (list< int >::iterator it = lst.begin(); it != lst.end(); ++it) cout << *it << " " ; cout << endl;
// Using auto with iterators and for loop: 1 2 3 4 5 for ( auto it = lst.begin(); it != lst.end(); ++it) cout << *it << " " ; cout << endl;
// Using range-based for loop: 1 2 3 4 5 for ( int i : lst) cout << i << " " ; cout << endl;
// Create a vector with some integers vector< int > v {1, 2, 3, 4}; // Do a bunch of stuff with the vector... // OLD WAY: Add some "random values" (verbose) v.push_back(7); v.push_back(11); v.push_back(18); v.push_back(-5); v.push_back(0); v.push_back(42);
// Create a vector with some integers vector< int > v {1, 2, 3, 4}; // Do a bunch of stuff with the vector... // NEW WAY: Add some random values (concise) for ( int i : {7, 11, 18, -5, 0, 42}) v.push_back(i);
  • Basically, any type that supports begin() , end() , and operator++ will work.
  • Built-in arrays now have corresponding global begin() and end() functions that will allow them to behave just like the standard containers (e.g. begin(array) )
  • Limitations:
  • Only works with for , not while or do...while .
// Print every other element: for ( int i = 0; i < len; i += 2 ) cout << a[i] << " " ;
  • Range-based for loops have limited use with static arrays and can't be used at all with dynamic arrays.
void fn( int *array) { // Won't work, array is a pointer for ( int i : array) cout << i; } error: invalid range expression of type 'int *'; no viable 'begin' function available for (int i : array) ^ ~~~~~
  • Because of these limitations, this technique is rarely used with static arrays. (Built-in static arrays are also used less these days because std::array is a better replacement.)
  • This technique was primarily invented for use with STL containers.
  • std::initializer_list will also work.
  • Range-for at Stroustrup's site.

nullptr ( N2431 )

#define NULL (( void *)0) // C #define NULL 0 // C++98
void foo( int ) { cout << "foo(int)" << endl; } void foo( int *) { cout << "foo(int *)" << endl; }
foo(0); // foo(int) foo( nullptr ); // foo(int*), C++11 foo(( int *)0); // foo(int*)
foo(NULL); // possibly ambiguous, depending on how NULL is defined
error: call to 'foo' is ambiguous foo(NULL); // possibly ambiguous, depending on how NULL is defined ^~~ note: candidate function void foo(int) ^ candidate function void foo(int *) ^ 1 error generated.
  • The nullptr can be used to compare to any pointer type.
char *p = nullptr ; . . . if (p) . . .
  • This can also help prevent ambiguities in template functions where the template parameter T may treat a 0 as an integer, instead of a null pointer.
  • See this for an example of ambiguity with overloaded functions taking pointers.
  • nullptr -- a null pointer literal at Stroustrup's site.

Strongly Typed Enums ( N2347 )

  • Create a new kind of enum that is strongly typed: enum class
  • Allow the programmer to choose the underlying type (i.e. sizeof ) used to represent the enum .
  • Allow access to the enum via the scope resolution operator.
enum ColorSpace {Red, Green, Blue}; // Represent graphics colors
ColorSpace cs = Green; // Set color to green (integer 1)
cs = 1; // ERROR: no conversion from integer
int i = Green; // OK, implicit conversion to integer, C++03 only
enum ColorSpace {Red, Green, Blue}; // Represent graphics colors enum TrafficLight {Red, Yellow, Green}; // Represent traffic signals
error: redefinition of enumerator 'Red' enum ColorSpace {Red, Green, Blue}; // Represent graphics colors ^ note: previous definition is here enum TrafficLight {Red, Yellow, Green}; // Represent traffic signals ^ error: redefinition of enumerator 'Green' enum ColorSpace {Red, Green, Blue}; // Represent graphics colors ^ note: previous definition is here enum TrafficLight {Red, Yellow, Green}; // Represent traffic signals ^
enum ColorSpace {csRed, csGreen, csBlue}; // Represent graphics colors enum TrafficLight {tlRed, tlYellow, tlGreen}; // Represent traffic signals
ColorSpace color = csGreen; // OK TrafficLight light = tlGreen; // OK
sizeof (ColorSpace)
// Elements are no longer ambiguous enum class ColorSpace {Red, Green, Blue}; // Represent graphics colors enum class TrafficLight {Red, Yellow, Green}; // Represent traffic signals // Clear, unambiguous, self-documenting ColorSpace cs = ColorSpace::Green; TrafficLight tl = TrafficLight::Green;
int i = Color::Green; // No conversion to int in C++11
enum TrafficLight {Red, Yellow, Green}; // Represent traffic signals TrafficLight tl = TrafficLight::Green; // Optional qualifier (recommended) TrafficLight tl2 = Green; // Same as TrafficLight::Green
enum class TrafficLight : char {Red, Yellow, Green}; // Represent traffic signals enum class ColorSpace : short {Red, Green, Blue}; // Represent graphics colors enum class Twister : long {Red, Green, Blue, Yellow}; // Represent the game colors enum class Checkers {Red, Black}; // Represent the game colors // Sizes shown are using the LP64 data model cout << sizeof (TrafficLight) << endl; // 1 cout << sizeof (ColorSpace) << endl; // 2 cout << sizeof (Twister) << endl; // 8 cout << sizeof (Checkers) << endl; // 4 (default type is int)
  • When you have a choice, use the scoped enumerations (enum classes) to avoid any ambiguities.
// Existing behavior, underlying type is compiler-dependent enum Color {Red, Yellow, Green}; cout << sizeof (Color) << endl; // 4 (defaults to int) cout << Color::Green << endl; // 2 (may be qualified, new in C++11) cout << Green << endl; // 2 (may be unqualified)
  • The rest are new in C++11:
// Specify underlying type enum Color : char {Red, Yellow, Green}; cout << sizeof (Color) << endl; // 1 cout << Color::Green << endl; // 2 (may be qualified) cout << Green << endl; // 2 (may be unqualified)
// Enumeration class (distinct type) enum class Color {Red, Yellow, Green}; cout << sizeof (Color) << endl; // 4 (defaults to int) cout << int (Color::Green) << endl; // 2 (Must be qualified, must cast, no operator<< for Color) cout << Green << endl; // Illegal, must be qualified
// Enumeration class (distinct type), specifying underlying type enum class Color : long {Red, Yellow, Green}; cout << sizeof (Color) << endl; // 8 cout << int (Color::Green) << endl; // 2 (Must be qualified, must cast, no operator<< for Color) cout << Green << endl; // Illegal, must be qualified
  • You should probably let the compiler choose the underlying representation, unless you have a good reason to override it (e.g. large arrays of enums and memory is very constrained.)
  • enum class -- scoped and strongly typed enums at Stroustrup's site.
enum class ColorSpace : short ; // Represent graphics colors (forward declaration) // Other code ... enum class ColorSpace : short {Red, Green, Blue}; // Actual definition is here
  • Obviously, the forward declaration must match that definition (i.e. underlying type) or it's an error.
  • Use when the compiler needs to know the size of the underlying type (for memory layout), but doesn't need to know the actual names or values of the members.
  • Only works with scoped enumerations (i.e. enum class ).
  • Read all of the gory details about it here .

auto Deductions ( N1984 )

auto i = 10; // int auto d = 10.0; // double auto f = 10.0F; // float
cout << typeid (i).name() << endl; // i cout << typeid (d).name() << endl; // d cout << typeid (f).name() << endl; // f
const auto ii = 10; // const int auto & ri = ii; // int& const auto & cri = ii; // const int&
auto i1 = 1; // int, as expected auto i2 = {1}; // initializer_list<int>, probably not what you want auto i3 {1}; // int, before C++14 it was initializer_list<int> auto i4(1); // int cout << typeid (i1).name() << endl; // i cout << typeid (i2).name() << endl; // St16initializer_listIiE cout << typeid (i3).name() << endl; // i cout << typeid (i4).name() << endl; // i
vector< int > v1; auto x1 = v1.crbegin(); cout << typeid (x1).name() << endl; vector< int >::const_reverse_iterator x2 = v1.crbegin(); cout << typeid (x2).name() << endl;
St16reverse_iteratorIN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEEE St16reverse_iteratorIN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEEE
// vector<int> auto v2(v1); cout << " auto v2(v1) is " << typeid (v2).name() << endl; // vector<int> auto v3 = v1; cout << " auto v3 = v1 is " << typeid (v3).name() << endl; // vector<int> auto v4{v1}; cout << " auto v4{v1} is " << typeid (v4).name() << endl; // initializer_list<vector<int>> auto v5 = {v1}; cout << "auto v5 = {v1} is " << typeid (v5).name() << endl; // vector<int> (no auto ) vector< int > v6 = {v1}; cout << " v6 = {v1} is " << typeid (v6).name() << endl;
auto v2(v1) is St6vectorIiSaIiEE auto v3 = v1 is St6vectorIiSaIiEE auto v4{v1} is St6vectorIiSaIiEE auto v5 = {v1} is St16initializer_listISt6vectorIiSaIiEEE v6 = {v1} is St6vectorIiSaIiEE
template < typename T> void print5a( const T& container) { for ( typename T::const_iterator iter = container.begin(); iter != container.end(); ++iter) std::cout << *iter << " " ; std::cout << std::endl; }
template < typename T> void print5b( const T& container) { for ( auto iter = container.begin(); iter != container.end(); ++iter) std::cout << *iter << " " ; std::cout << std::endl; }
template < typename T> void print5( const T& container) { for ( auto e : container) std::cout << e << " " ; std::cout << std::endl; }
auto i = 5, j = 7, *pi = &i; // OK auto k = 100, d = 3.1415; // error: inconsistent deduction for auto vector< int > v1 {1, 2, 3}; for ( auto iter = v1.begin(), end = v1.end(); iter != end; ++iter) cout << *iter << endl;
  • Before C++11, the auto keyword was used as a storage specifier. However, it really went unused for decades so is unlikely to prove problematic with existing code.
  • The auto keyword is really meant for long and difficult types (think templates and lambda functions). Although it works for all types, it has little benefit with simple types (e.g. int , double , etc.).
auto x = SomeFunc(); // You must read the documentation or header file.
  • As Herb Sutter says "Treat a new C++ feature like you would treat a loaded automatic weapon in a crowded room: never use it just because it looks nifty. Wait until you understand the consequences."

Multiple auto Declarations ( N1737 )

Raw and Unicode String Literals ( N2442 )

    "('(?:[^\\\\']|\\\\.)*'|\"(?:[^ \\\\\ "]|\\\\.)*\")|" Are the high-lighted five backslashes correct or not? Even experts become easily confused. Here is the same line as a raw string literal:      R"(('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|)"
std::string text_editor( "c:\windows\system32\notepad.exe" );
error: unknown escape sequence '\w' [-Werror,-Wunknown-escape-sequence] std::string text_editor("c:\windows\system32\notepad.exe"); ^~ error: unknown escape sequence '\s' [-Werror,-Wunknown-escape-sequence] std::string text_editor("c:\windows\system32\notepad.exe"); ^~ 2 errors generated.
std::string text_editor( "c:\\windows\\system32\\notepad.exe" );
std::string text_editor(R "(c:\windows\system32\notepad.exe)" );
  • A raw string must start with R" (capital 'R' only, not lowercase 'r') and a double-quote character.
  • The R" is then followed by a PREFIX which consists of 0 to 16 characters. Most characters are allowed.
  • The (optional) PREFIX must be followed by a left parenthesis.
  • After the left parenthesis, the actual characters of the string will follow and no interpretation is done, i.e. backslashes do not escape anything and double-quotes do not terminate the raw string but are part of it.
  • The raw string is terminated by a right parenthesis followed by the same PREFIX that was specified in #2.
  • Finally, a double-quote character terminates the entire sequence.
const char *s1 = R"( hello )"; // hello const char *s2 = R"( he"ll"o )"; // he"ll"o const char *s3 = R"( hel)lo )"; // hel)lo const char *s4 = R"( h\tello\n )"; // h\tello\n const char *s5 = R"( R"( )"; // R"( const char *s6 = R"( R"( )")"; // ERROR
hello he"ll"o hel)lo h\tello\n R"(
cpp14.cpp:562:29: error: missing terminating '"' character [-Werror,-Winvalid-pp-token] const char *s6 = R"(R"()")"; // ERROR ^ cpp14.cpp:562:28: error: expected ';' at end of declaration const char *s6 = R"(R"()")"; // ERROR ^ ; 2 errors generated.
R"(R")")" ^^
const char *s7 = R"x( R"()" )x"; // R"()"

Non-static Data Member Initializers ( N2756 )

// Global function int getnumber() { return 42; } class A { public : A() : b{7, 8, 9} // initialized by member initializer list { // Assignment, not initialization (OK, c is non-const) for ( int i = 0; i < 3; i++) c[i] = i; } A( int x) : i(x), b{x, x, x} { // c is left uninitialized } void print() { cout << i << endl; cout << j << endl; cout << k << endl; cout << d << endl; for ( auto it : v) cout << it << " " ; cout << endl; for ( auto it : a) cout << it << " " ; cout << endl; for ( auto it : b) cout << it << " " ; cout << endl; for ( auto it : c) cout << it << " " ; cout << endl; } private : int i = 5; // Member initialization int i2 {6}; // Member initialization (New in C++11) int i3 = {7}; // Member initialization int i4(8); // This syntax won't work here. * See note below int j = getnumber(); // Member initialization (calls member function) int k = ::getnumber(); // Member initialization (calls global function) double d = 3.14; // Member initialization vector< int > v{1, 2, 3}; // Member initialization (vector constructor) const int a[3] {0, 0, 0}; // const can be initialized here const int b[3]; // or using the member initializer list in the constructor int c[3]; // Non-const can be assigned in the constructor // private member function int getnumber() { return 32; } };
  • * According to Stroustrup's The C++ Programming Language, 4th Edition , section 17.4.4 - "For pretty obscure technical reasons related to parsing and name lookup, the {} and = initializer notations can be used for in-class member initializers, but the () cannot."
  • The benefit of this is more obvious when you have multiple constructors that all need to initialize the private members.
  • N2756 Non-static data member initializers
  • In-class member initializers from Stroustrup's web site.

Trailing Return Types ( N3276 )

I'm going to introduce some new syntax using trivial examples. However, these techniques should never be used for trivial functions, as they cause more problems than they solve. Actually, they don't solve anything, they just create problems. But, before I show you the advanced reasons for using these techniques, I want you to understand what's going on.

int Add( int a, int b) { return a + b; }
auto Add( int a, int b) -> int { return a + b; }
foo( int , int , int ) -> long ; foo( const char *) -> std::string;
template < typename T, typename U> auto Add(T a, U b) -> decltype (a + b) { return a + b; }
int i1 = 5, i2 = 6; double d1 = 2.72, d2 = 3.14; cout << Add(i1, i2) << endl; // return type is int cout << Add(d1, d2) << endl; // return type is double cout << Add(i1, d2) << endl; // return type is double
template < typename T, typename U> decltype (a + b) Add(T a, U b) { return a + b; }
error: use of undeclared identifier 'a' decltype(a + b) Add(T a, U b) -> ^ error: use of undeclared identifier 'b' decltype(a + b) Add(T a, U b) -> ^
auto Add( int a, int b) { return a + b; }
template < typename T, typename U> auto Add(T a, U b) { return a + b; // The compiler figures out the return type }
template < typename T, typename U> auto max(T a, U b) { if (a > b) return a; else return b; // a and b can be different types } cout << max(1, 2.0) << endl; // Call max with different types
error: 'auto' in return type deduced as 'double' here but deduced as 'int' in earlier return statement return b; ^
  • The primary motivation for this was to help template functions deduce different return types. (It just happens to work with non-template functions, as well.)
  • Another motivation was for lambda expressions, where deducing the return types can be quite complex.
  • Using auto for functions that have a fixed return type, introduces problems:
  • Putting the prototype in a header is meaningless:
auto SomeFunc(); // Not enough information for the compiler.
  • It's useless to a programmer as well as there isn't enough information. Now the programmer is forced to find the documentation and read it. Good luck with that! (The source code may not even be available, either.)
  • Speaking of documentation, automatic documentation generators (e.g. Doxygen) won't be able to figure out the return type either, so even if there were documentation, it wouldn't have that information.
  • Should I go on?
  • Bottom line: If you're writing template functions with templated return types or writing lambda functions, you can use it. Otherwise, don't. Just. Don't.

__func__ Predefined Identifier ( N2340 )

void SomeFunction() { cout << "Function name is: " << __FUNCTION__ << endl; // Old non-standard extension }
Function name is: SomeFunction
cout << "In function: " << __func__ << endl; // The name of the function cout << " in file: " << __FILE__ << endl; // The name of the C++ file cout << " at line: " << __LINE__ << endl; // The current line number cout << " on: " << __DATE__ << endl; // The current date cout << " at: " << __TIME__ << endl; // The current time
In function: f13 in file: cpp14.cpp at line: 991 on: Apr 16 2018 at: 14:01:20
cout << "In function: " << __func__ << endl; cout << " in file: " << "cpp14.cpp" << endl; cout << " at line: " << 991 << endl; cout << " on: " << "Apr 16 2018" << endl; cout << " at: " << "14:03:40" << endl;
cout << "An out-of-range error has occurred in file " << __FILE__ << " at line " << __LINE__ << " (in function " << __func__ << ").\n" << "The software was last compiled on " << __DATE__ << " at " << __TIME__ << ".\n" ;
An out-of-range error has occurred in file cpp14.cpp at line 997 (in function f13). The software was last compiled on Apr 16 2018 at 14:05:14.
  • No name decorating (mangling) is done on the names.
  • Overloaded functions and templated functions just print out the function name. There is no way to disambiguate them.
operator ()

Delegating Constructors ( N1986 )

class C1 { public : // Default constructor C1() { init(); // Do other setup code... } // Single-argument conversion constructor C1( double x) : d_(x) { init(); // Do other setup code... } // Non-default constructor C1( int a, int b, int c, double d) : a_(a), b_(b), c_(c), d_(d) { init(); // Do other setup code... } private : int a_, b_, c_; double d_; void init() { // Do a lot of setup stuff } };
class C2 { public : // Default constructor C2() : C2(0, 0, 0, 0.0) // delegate to other constructor { } // Single-argument conversion constructor C2( double x) : C2(0, 0, 0, x) // delegate to other constructor { } // Non-default constructor C2( int a, int b, int c, double d) : a_(a), b_(b), c_(c), d_(d) // maybe more initialization here... { // Do a lot of setup stuff } private : int a_, b_, c_; double d_; };
C2() : C2(0, 0, 0, 0.0), a_(0) // Can't do both { }
error: an initializer for a delegating constructor must appear alone C2() : C2(0, 0, 0, 0.0), a_(0) // Can't do both ^~~~~~~~~~~~~~~~ ~~~~~ 1 error generated.
  • In this small example there may be little to be gained by using a delegate constructor. You have to imagine that the class (and corresponding constructors) is more complex than this simple, contrived example pretends to be. However, the points made should be clear.

noexcept ( )

int lookup( const vector< int >& v, int index) throw (std::out_of_range) { return v.at(index); // This method does range checking }
throw (std::out_of_range)
vector< int > v {10, 20, 30}; try { cout << lookup(v, 2) << endl; // Prints 30 cout << lookup(v, 5) << endl; // Throws a std::out_of_range exception } catch ( const std::out_of_range& ex) { cout << ex.what() << endl; // Display details about the exception }
30 vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
// foo1 will throw no exceptions int foo1() throw (); // foo2 will only throw std::out_of_range or std:bad_alloc int foo2() throw (std::out_of_range, std::bad_alloc); // foo3 may thrown any exception int foo3();
  • foo1 - An empty exception specification may not throw any exception.
  • foo2 - A non-empty exception specification may only throw the exceptions listed.
  • foo3 - A missing exception specification may throw any exception.
// Function will not throw an exception void foobar1() noexcept ( true ) { // do something safe } // Function may throw an exception void foobar2() noexcept ( false ) { // do something possibly unsafe } // foobar3 may throw an exception only if foobar2 may throw an exception void foobar3() noexcept ( noexcept (foobar2())) { foobar2(); // call possibly unsafe function } // foobar4 will not throw an exception (protects unsafe call to foobar2) void foobar4() noexcept ( true ) { try { foobar2(); // call possibly unsafe function } catch (... /* whatever foobar2 throws */ ) { // do something with the exception } // safely continue excecuting... }
cout << boolalpha; cout << " noexcept(true)? " << noexcept ( true ) << endl; cout << " noexcept(false)? " << noexcept ( false ) << endl; cout << "foobar1 noexcept? " << noexcept (foobar1()) << endl; cout << "foobar2 noexcept? " << noexcept (foobar2()) << endl; cout << "foobar3 noexcept? " << noexcept (foobar3()) << endl; cout << "foobar4 noexcept? " << noexcept (foobar4()) << endl;
noexcept(true)? true noexcept(false)? true foobar1 noexcept? true foobar2 noexcept? false foobar3 noexcept? false foobar4 noexcept? true
void foobar() noexcept ( true ) // explicit void foobar() noexcept // implicit
Q: "Do you want me to not turn the lights off?" A: Yes, please do not turn them off.
Q: "Do you want me to turn the lights off?" A: No, please do not turn them off.
  • Destructors are now noexcept by default unless a member or base class has a destructor marked as noexcept(false) .
  • C++11 (and later) refers to the old style throw(...) as dynamic exception specifications and the newer one as just exception specifications .
  • "Move constructors of all the types used with STL containers, for example, need to be declared noexcept . Otherwise STL will choose copy constructors instead. The same is valid for move assignment operations."
void foobar() noexcept ( true ) void foobar() noexcept ( false ) // redefinition

Generalized constexpr ( N2235 )

const int i = getvalue1(); // Sets i at runtime. Must be initialized because it's const int array[i]; // Illegal, i isn't known at compile time.
constexpr int ipower( int base, int exponent) { int p = 1; for ( int i = 0; i < exponent; i++) p *= base; return p; } constexpr int squared( int x) { return x * x; } int getvalue1() { return 10; } constexpr int getvalue2() { return 20; }
int a1[getvalue1()]; // Illegal, getvalue1() isn't known at compile-time int a2[getvalue2()]; // OK, constant expression const int v1 = getvalue1(); // OK, but v1 isn't known at compile-time (not constexpr) int a3[v1]; // Illegal const int v2 = getvalue2(); // OK, but v2 is not constexpr int a4[v2]; // Illegal constexpr int v3 = getvalue1(); // Illegal, can't initialize v3 with non-constexpr constexpr int v4 = getvalue2(); // OK int a5[v4]; // OK int a6[squared(5)]; // OK cout << sizeof (a6) << endl; // prints 100 (25 * 4) int a7[squared(getvalue1())]; // Illegal, getvalue1 is not constexpr int a8[squared(getvalue2())]; // OK cout << sizeof (a8) << endl; // prints 1600 (400 * 4) int a9[ipower(2, 3)]; // OK cout << sizeof (a9) << endl; // prints 32 (8 * 4)
  • Functions that are specified as constexpr must be seen by the compiler and will be inlined at the call.
  • This means that constexpr functions are usually implemented in header files. (They are implicitly inlined).
  • Member functions (including constructors and trivial destructors, with some restrictions) can be constexpr .
return some-expression ;
constexpr int v4 = getvalue2(); // OK v4 = 5; // Illegal, v4 is implicitly const
  • array bounds
  • expressions in case-statements
  • initialization of enumerators in enumeration definitions
  • values specified for non-type template parameters
  • There is a lot more to know about constexpr and you can read more about it here: Stroustrup's C++11FAQ and Generalized Constant Expressions

Template Aliases ( N2258 )

typedef unsigned char BYTE; typedef short int FAST_INT; typedef float CURRENCY; typedef unsigned char * PCHAR;
BYTE next, previous; // For scanning bytes in memory CURRENCY tax, discount; // To calculate total price PCHAR inbuf, outbuf; // To manipulate strings
using BYTE = unsigned char ; using FAST_INT = short int ; using CURRENCY = float ; using PCHAR = unsigned char *;
typedef vector< int > MyVectorI; // MyVectorI is an alias for vector of int typedef vector<string> MyVectorS; // MyVectorS is an alias for vector of string typedef vector< double > MyVectorD; // MyVectorD is an alias for vector of double
MyVectorI mvi; // vector of int MyVectorS mvs; // vector of string MyVectorD mvd; // vector of double
template < typename T> typedef vector<T> MyVectorOfT;
error: a typedef cannot be a template typedef vector MyVectorOfT; ^ 1 error generated.
template < typename T> using MyVector = vector<T>; // MyVector is an alias for a vector anything
MyVector< int > vi2; // vector of int MyVector<string> vs2; // vector of string MyVector< double > vd2; // vector of double
template < typename T> using MyVectorT = vector<list<T>>; // MyVectorT is an alias for a vector of list of anything MyVectorT< int > vli; // vector of list of int MyVectorT<string> vls; // vector of list string MyVectorT< double > vli; // vector of list of double
template < typename T, typename Alloc = std::allocator<T>> using MyIter = typename vector<list<T, Alloc>>::iterator; MyIter<string> it1; // An iterator on a vector of lists of string and the default allocator MyIter<Foo, MyAlloc> it2; // An iterator on a vector of lists of Foo and custom allocator
  • template alias at Stroustrup's site.

Lambdas ( N2927 )

Long Long Data Type ( N1811 )

  • The long long type allows implementations to use more than 64-bits, possibly 128-bits, for an integral type.
  • It allows Microsoft's code to work with the rest of the world. Microsoft's Windows OS is currently the only popular 64-bit system that still uses 32-bits for long integers. Programmers on Windows should use long long if they want a 64-bit data type. ( 64-bit data models shows the sizes of data types on common systems.)
Type Bytes Also called Range of values (Binary) Range of values (Decimal) signed long int 8 long long int signed long -2 63 to 2 63 - 1 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 unsigned long int 8 unsigned long 0 to 2 64 - 1 0 to 18,446,744,073,709,551,615 signed long int 4 long long int signed long -2 31 to 2 31 - 1 -2,147,483,648 to 2,147,483,647 unsigned long int 4 unsigned long 0 to 2 32 - 1 0 to 4,294,967,295
long long ll = 12345LL; // LL for (signed) long long unsigned long long ull = 12345ULL; // ULL for unsigned long long
cin >> ll; cin >> ull; cout << ll << endl; cout << ull << endl;
scanf( "%lli" , &ll); // Can also use %lld scanf( "%llu" , &ull); printf( "%lli\n" , ll); // Can also use %lld printf( "%llu\n" , ull);

Default and Deleted Functions ( N2346 )

  • A default constructor (that does nothing)
  • A destructor (that does nothing)
  • A copy constructor (that does memberwise copy)
  • A copy assignment operator (that does memberwise assignment)
class Foo4 { public : friend ostream& operator <<(ostream& os, const Foo4& rhs); private : int a = rand() % 10; int b = rand() % 10; }; ostream& operator <<(ostream& os, const Foo4& rhs) { os << rhs.a << ", " << rhs.b; return os; }
void f23() { Foo4 f1; // default constructor (compiler-generated) Foo4 f2; // default constructor (compiler-generated) Foo4 f3(f1); // copy constructor (compiler-generated) cout << "f1: " << f1 << endl; // f1: 3, 6 cout << "f2: " << f2 << endl; // f2: 7, 5 cout << "f3: " << f3 << endl; // f3: 3, 6 f1 = f2; // copy assignment (compiler-generated) cout << "f1: " << f1 << endl; // f1: 7, 5 } // destructor (compiler-generated) called for f1, f2, f3
class Foo4 { public : Foo4( int i, int j) : a(i), b(j) {} // non-default constructor friend ostream& operator <<(ostream& os, const Foo4& rhs); private : int a = rand() % 10; int b = rand() % 10; };
Foo4 f1; // default constructor
error: no matching constructor for initialization of 'Foo4' Foo4 f1; // default constructor
Foo4 f4(1, 2); // non-default constructor cout << "f4: " << f4 << endl; // f4: 1, 2
class Foo4 { public : Foo4() {} // default constructor (programmer-implemented, does nothing) Foo4( int i, int j) : a(i), b(j) {} ... };
class Foo4 { public : Foo4() = default ; // compiler will generate this Foo4( int i, int j) : a(i), b(j) {} ... };
class Foo4 { public : virtual ~Foo4() {} // empty destructor to get virtual behavior ... };
class Foo4 { public : virtual ~Foo4() = default // compiler will generate the virtual destructor ... };
class Foo4 { public : ... private : Foo4( const Foo4& rhs); // copy constructor, do not implement! int a = rand() % 10; int b = rand() % 10; };
Foo4 f3(f1); // call copy constructor
error: calling a private constructor of class 'Foo4' Foo4 f3(f1); // copy constructor (compiler-generated) ^
class Foo4 { public : Foo4( const Foo4& rhs) = delete ; // copy constructor won't be generated ... };
error: call to deleted constructor of 'Foo4' Foo4 f3(f1); // copy constructor (compiler-generated) ^ ~~ note: 'Foo4' has been explicitly marked deleted here Foo4(const Foo4&) = delete; ^
friend ostream& operator <<(ostream os, const Foo4& rhs);
error: call to deleted constructor of 'ostream' (aka 'basic_ostream ') cout << "f1: " << f1 << endl; // f1: 3, 6 ^~~~~~~~~~~~~~
bool isEven( long value) { if (value % 2) return false ; else return true ; }
long j = 8; float f = 8.0F; double d = 8.0; cout << boolalpha << isEven(j) << endl; // long, OK (true) cout << boolalpha << isEven(f) << endl; // float to long, OK (true) cout << boolalpha << isEven(d) << endl; // double to long, (true)
bool isEven( float ) = delete ; // declaration only bool isEven( double ) = delete ; // declaration only
error: use of deleted function 'bool isEven(float)' cout << boolalpha << isEven(f) << endl; // float to long, OK ^ note: declared here bool isEven(float) = delete; ^ error: use of deleted function 'bool isEven(double)' cout << boolalpha << isEven(d) << endl; // double to long, OK ^ note: declared here bool isEven(double) = delete; ^
#include <array>
array< double , 5> ar; // ar represents a static array of 5 doubles, e.g. double ar[5];
array< int , 3> a1; // undefined values: 32615, 1876, 3 int s1[3]; // undefined values: -891498544, 32767, 6299136
array< int , 3> a2{}; // default initialized to 0 (default constructor) int s2[3] = {}; // default initialized to 0 (Not legal in C, use {0})
array< int , 3> a3{1, 2, 3}; // OK int s3[3] = {1, 2, 3}; // OK
array< int , 3> a4{1, 2, 3, 4}; // ERROR: too many initializers int s4[3] = {1, 2, 3, 4}; // ERROR: too many initializers
array< int > a5{1, 2, 3, 4}; // ERROR: no size specified (compiler won't help here) int s5[] = {1, 2, 3, 4}; // OK: compiler sets size to 4 from initializers
In function 'void f1()': error: wrong number of template arguments (1, should be 2) array a5{1, 2, 3, 4}; // ERROR: no size specified ^
array< int , 3> a4{1, 2}; // Too few initializers, rest are 0 int s4[3] = {1, 2}; // Too few initializers, rest are 0
int s5[] = {1, 2, 3, 4}; // The compiler will figure out the size. std::array ar {1, 2, 3, 4}; // Error! Must specify type and size
// New in C++17. Class Template Argument Deduction // ar is deduced as std::array<int, 4> std::array ar {1, 2, 3, 4};
array< int > ar5 {1, 2, 3}; // ERROR, too few template arguments
array ar1 {1, 2, 3}; // OK, size is 3, values: 1,2,3 array< int , 3> ar2 {1, 2, 3}; // OK, size is 3, values: 1,2,3 array< int , 5> ar3 {1, 2, 3}; // OK, size is 5, values: 1,2,3,0,0 array< int , 5> ar4; // OK, size is 5, values are all undefined array< int , 5> ar5 {}; // OK, size is 5, values: 0,0,0,0,0 array< int , 2> ar6 {1, 2, 3}; // ERROR, too many initializers array< int > ar7 {1, 2, 3}; // ERROR, too few template arguments array ar8; // ERROR, can't deduce template parameters
BTW, class argument template deduction can be a pretty complex thing, but you don't have to understand any of it to use it correctly with std::array . For more information, follow these links. But be aware, it is not for the faint-of-heart! Template argument deduction for class templates (P0091R3) . Filling holes in Class Template Argument Deduction (P1021R5) .
void print( const int *arr, int size) { for ( int i = 0; i < size; i++) std::cout << arr[i] << std::endl; }
void print3ints1( const std::array< int , 3>& arr) { for ( int i = 0; i < 3; i++) std::cout << arr[i] << std::endl; }
void print3ints2( const std::array< int , 3>& arr) { for (size_t i = 0; i < arr.size() ; i++) std::cout << arr[i] << std::endl; }
void print3ints3( const std::array< int , 3>& arr) { for ( int i : arr) std::cout << i << std::endl; }
array< int , 2> a7{1, 2}; // array of 2 ints array< double , 3> a8{1.1, 2.2, 3.3}; // array of 3 doubles
void print2ints( const std::array< int , 2>& arr) { for ( int i : arr) std::cout << i << std::endl; } void print4doubles( const std::array< double , 4>& arr) { for ( double d : arr) std::cout << d << std::endl; }
template < typename T, size_t size> void print( const std::array<T, size>& arr) { for ( const T& t : arr) std::cout << t << std::endl; }
  • The std::array knows its size - With built-in arrays, we always had to store the size in some other variable and pass it as a separate argument to a function (and hope that the value is accurate). Since std::array is a class, its size is known to each object. Just call the size() method. (The size may not even be stored in the object!)
  • Bounds checking - Because it knows its size, bounds checking can be performed when using the at method (similar to std::vector ). The subscript operator is supported, of course, but does not do any bounds-checking.
  • Has begin() and end() members to work well with the standard algorithms (e.g. sort)
  • You can assign one std::array to another. With a built-in array, assignment is illegal. You must perform some kind of looping to copy one element at a time.
  • You can return a std::array from a function. With a built-in array, you can only return a pointer to the first element.
  • A std::array doesn't decay into a pointer like built-in arrays do, which is a well-known source of confusion, especially with novice programmers. (However, if you need it, you can get the pointer with something like this: &array[0] ).
  • Keep in mind that there is no overhead at all when using a std::array in place of a built-in array, so you are not incurring any performance penalty.
  • The size of the std::array is known at compile time, so there is zero runtime overhead to create one. std::vector , on the other hand, must be dynamically allocated at runtime, which may incur a significant overhead.
  • A std::vector has additional members which require more memory. A std::array contains only the static array itself.
  • A std::vector can grow and shrink at runtime to accommodate more/less elements. The size of a std::array is fixed and can't grow or shrink.
  • A std::array requires the type being stored to have a default constructor (if not given explicit initializers). A std::vector does not.
  • Because the size of a std::array must be known at compile time, it's less flexible than a std::vector (but more efficient). Note that there is no push_back , insert , or erase / remove methods because the array cannot grow or shrink.
  • If you know the size (number of elements) that will be stored at compile time and that number will not change at runtime, use a std::array .
  • If you don't know the number of elements at compile time, or the size will change at runtime, use a std::vector .

Structured Bindings ( P0144R2 )

Note: This was added in C++ 17 so, depending on the version of your compiler, you may need to use std=c++17 for these examples to work.
struct Student { int age; double gpa; };
// x is an int and gets the value 42 // y is a double and gets the value 3.14 // x and y are copies of the structure members auto [x, y] = student; cout << "x is " << x << ", y is " << y << endl; cout << "student.age is " << student.age << ", student.gpa is " << student.gpa << endl;
x is 42, y is 3.14 student.age is 42, student.gpa is 3.14
int [x, y] = struct2ints; // assume the structure only contains 2 integers
error: structured binding declaration cannot have type 'int' int [x, y] = struct2ints; // assume the structure only contains 2 integers ^~~~~~ note: type must be cv-qualified 'auto' or reference to cv-qualified 'auto'
This is another reason why the auto keyword was (re)introduced into the language. The syntax would be unnecessarily complicated if each structure member had a different type, as is the case with most structures.
// Modifying x and y does not affect s.age or s.gpa in any way x = 10; y = 3.75; cout << "x is " << x << ", y is " << y << endl; cout << "student.age is " << student.age << ", student.gpa is " << student.gpa << endl;
x is 10, y is 3.75 student.age is 42, student.gpa is 3.14
// rx and ry are references auto & [rx, ry] = student; rx = 10; // Modifies rx and s.age (they refer to the same object) student.gpa = 3.75; // Modifies s.gpa and ry (they refer to the same object) cout << "rx is " << rx << ", ry is " << ry << endl; cout << "student.age is " << student.age << ", student.gpa is " << student.gpa << endl;
rx is 10, ry is 3.75 student.age is 10, student.gpa is 3.75
cout << " Address of rx is " << &rx << endl; cout << "Address of student.age is " << &student.age << endl; cout << " Address of ry is " << &ry << endl; cout << "Address of student.gpa is " << &student.gpa << endl;
Address of rx is 0x7fff8bb5c860 Address of student.age is 0x7fff8bb5c860 Address of ry is 0x7fff8bb5c868 Address of student.gpa is 0x7fff8bb5c868
auto [x] = student; // Error, not enough variables auto [i, j, k] = student; // Error, too many variables
error: only 1 name provided for structured binding auto [x] = student; ^~~ note: while 'Student' decomposes into 2 elements
error: 3 names provided for structured binding auto [i, j, k] = student; ^~~~~~~~~ note: while 'Student' decomposes into 2 elements
Student create_student( int age, double gpa) { return Student {age, gpa}; }
// age is 42, gpa is 3.14 auto [age, gpa] = create_student(42, 3.14);
// sizeof(array) is known at compile-time int array[] {1, 2, 3}; // a is 1, b is 2, c is 3 auto [a, b, c] = array;
std::array< int , 3> foo( const std::array< int , 4>& array) { // The variables contain copies of the 4 elements in array auto [a, b, c, d] = array; // Create an array with the first 3 elements above and return it return std::array< int , 3> {a, b, c}; }
std::array< int , 3> retval = foo(std::array{1, 2, 3, 4}); // x is 1, y is 2, z is 3 auto [x, y, z] = retval;
// sizeof(array) is known at compile-time int array[] {1, 2, 3}; // Only extract the first two elements (ignore the third element) auto [a, b] = array;
// sizeof(array) is known at compile-time int array[] {1, 2, 3}; // Only extract the first and third element (ignore the second) auto [a, ,b] = array;
      
// Error codes for quadratic formula enum MATHERR {ERR_NONE, ERR_NEGSQRT, ERR_DIVZERO}; // A function that takes 3 doubles and returns a tuple (struct) that consists of // the two calculated values (roots) and a (possible) error code std::tuple< double , double , MATHERR> quadratic( double a, double b, double c) { // Prevent dividing by zero if (a == 0) return {0.0, 0.0, ERR_DIVZERO}; // Construct tuple with error code and return it // b^2 - 4ac double discriminant = b * b - 4 * a * c; // Prevent taking the square root of a negative number if (discriminant < 0) return {0.0, 0.0, ERR_NEGSQRT}; // Construct tuple with error code and return it // All safe operations now double pos_numerator = -b + std::sqrt(discriminant); double neg_numerator = -b - std::sqrt(discriminant); double denominator = 2 * a; // Return 3 pieces of information, no errors return {pos_numerator / denominator, neg_numerator / denominator, ERR_NONE}; }
void f36() { // Setup some test values double a = -3.1; double b = 5.2; double c = 1.3; cout << "Applying quadratic formula to a, b, c (" << a << ", " << b << ", " << c << ")" << endl; // Call the function and retrieve 3 results auto [root1, root2, errcode] = quadratic(a, b, c); // Check if there were any errors if (errcode == ERR_NONE) cout << "Roots are: " << root1 << ", " << root2 << endl; else if (errcode == ERR_DIVZERO) cout << "Divide by zero." << endl; else if (errcode == ERR_NEGSQRT) cout << "Sqrt of negative." << endl; else cout << "Unknown error." << endl; // shouldn't happen }
Applying quadratic formula to a, b, c (-3.1, 5.2, 1.3) Roots are: -0.220908, 1.89833
Applying quadratic formula to a, b, c (1, 2, 3) Sqrt of negative. Applying quadratic formula to a, b, c (0, 2, 3) Divide by zero. Applying quadratic formula to a, b, c (1, 4, 3) Roots are: -1, -3
void f37() { // Setup some values double a = -3.1; double b = 5.2; double c = 1.3; cout << "Applying quadratic formula to a, b, c (" << a << ", " << b << ", " << c << ")" << endl; // Switch on the error code using select statement initialization (P0305R1) switch ( auto [root1, root2, errcode] = quadratic(a, b, c); errcode) { case ERR_NONE: cout << "Roots are: " << root1 << ", " << root2 << endl; break ; case ERR_DIVZERO: cout << "Divide by zero." << endl; break ; case ERR_NEGSQRT: cout << "Sqrt of negative." << endl; break ; default : cout << "Unknown error." << endl; } }

Generalized Attributes ( N2761 )

Correct usage (safe) Incorrect usage (unsafe) char *p = ( char *)malloc(128); // allocate 128 bytes // use the memory ... free(p); // release the memory
malloc(128); // allocate 128 bytes // Can't use the memory // Can't free the memory (leak)
warning: ignoring return value of 'void* malloc(size_t)', declared with attribute warn_unused_result [-Wunused-result] 537 | malloc(128); // allocate 128 bytes | ~~~~~~^~~~~
Node structure Helper function // Node for a singly linked list struct Node { int data; // arbitrary data in the node Node *next; // next node in the list };
// helper function to create a Node (no error handling) Node *MakeNode( int value) { Node *node = new Node; // allocate memory node->data = value; // assign the data node->next = nullptr ; // set next to NULL return node; // return memory to the caller }
Typical use (safe): Incorrect use (unsafe): // call helper to allocate node Node *p = MakeNode(42); // ... delete p; // done with the node
// call helper to allocate node // but ignore the returned value // (memory leak) MakeNode(42);
// helper function to create a Node (no error handling) [[nodiscard]] Node *MakeNode( int value) { Node *node = new Node; // allocate memory node->data = value; // assign the data node->next = nullptr ; // set next to NULL return node; // return memory to the caller }
MakeNode(42); // Ignore important return value
warning: ignoring return value of 'Node* MakeNode(int)', declared with attribute nodiscard [-Wunused-result] MakeNode(42); ~~~~~~~~^~~~ note: declared here [[nodiscard]] Node *MakeNode(int value) ^~~~~~~~
Warnings emitted: Warnings suppressed: switch (year) { case 1: freshman++; case 2: sophomore++; printf( "Found lower division student.\n" ); break ; case 3: junior++; case 4: senior++; printf( "Found upper division student\n" ); break ; default : printf( "Invalid year\n" ); break ; }
switch (year) { case 1: freshman++; [[ fallthrough ]]; // suppress fall through warning case 2: sophomore++; printf( "Found lower division student.\n" ); break ; case 3: junior++; [[ fallthrough ]]; // suppress fall through warning case 4: senior++; printf( "Found upper division student\n" ); break ; default : printf( "Invalid year\n" ); break ; }
warning: this statement may fall through [-Wimplicit-fallthrough=] freshman++; ~~~~~~~~^~ note: here case 2: ^~~~ warning: this statement may fall through [-Wimplicit-fallthrough=] junior++; ~~~~~~^~ note: here case 4: ^~~~
switch (year) { case 1: // No warning case 2: lower++; printf( "Found lower division student.\n" ); break ; case 3: // No warning case 4: upper++; printf( "Found upper division student\n" ); break ; default : printf( "Invalid year\n" ); break ; }
  • Attribute specifier sequence — Shows the list of standard attributes .
  • General Attributes for C++ (N2761) — The detailed standards document.

Stricter Expression Evaluation Order ( P0145R3 )

int f1() { cout << "f1" << endl; return 1; } int f2() { cout << "f2" << endl; return 2; } int f3() { cout << "f3" << endl; return 3; } int main() { // add only int x = f1() + f2() + f3(); cout << "x is " << x << endl; // add and multiply int y = f1() + f2() * f3(); cout << "y is " << y << endl; return 0; } Compiler #1: f1 f2 f3 x is 6 f1 f2 f3 y is 7 Compiler #2: f1 f2 f3 x is 6 f2 f3 f1 y is 7
int main() { // What is the output? cout << f1() << f2() << f3() << endl; return 0; } Output #1: f3 f2 f1 123 Output #2: f1 1f2 2f3 3
Compiler/Output #1 Compiler/Output #2 GNU g++ 4.8, 5, and 6 MS VS 12 MS VS 14 MS VS 19 (with default: c++14) GNU g++ 7, 8, and 9 Clang 3.5, 3.8, 8 MS VS 19 (with /std:c++17)
int main() { try { cout << "Value is: " << somefn() << endl; } catch ( int ) { cout << "Exception thrown" << endl; } return 0; } // This function may throw an exception int somefn() { // Do something here ... // If something goes wrong... throw 5; // Everything is fine return 5; }
Value is: 5
Output from pre-C++17 compiler: Exception thrown
Value is: Exception thrown

Inheriting Constructors ( N2540 )

  • Clang's support for C++98, C++11, C++14, and C++1z.
  • GCC's support for C++11 , C++14 , C++17 , C++20 , C++23 , All .
  • Microsoft's support for Modern C++ .
  • Additional compiler support for all modern versions.
  • C++11FAQ at Stroustrup's site.
Write What You Know, and Know What You Write Advice From the C++ Experts: My best advice? Don't get fancy. Treat a new C++ feature like you would treat a loaded automatic weapon in a crowded room: never use it just because it looks nifty. Wait until you understand the consequences, don't get cute, write what you know, and know what you write. The article is a little out-dated, but most of the advice is still excellent, especially for new programmers that like to use language features that they don't really understand yet.
  • Create Account

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,372 software developers and data experts.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use .

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.

  • Windows Programming
  • UNIX/Linux Programming
  • General C++ Programming
  • candidate function not viable:

  candidate function not viable:

(the implicit copy assignment operator) not viable

Copy assignment operator

A copy assignment operator of class T is a non-template non-static member function with the name operator = that takes exactly one parameter of type T , T & , const T & , volatile T & , or const volatile T & . A type with a public copy assignment operator is CopyAssignable .

[ edit ] Syntax

[ edit ] explanation.

  • Typical declaration of a copy assignment operator when copy-and-swap idiom can be used
  • Typical declaration of a copy assignment operator when copy-and-swap idiom cannot be used
  • Forcing a copy assignment operator to be generated by the compiler
  • Avoiding implicit copy assignment

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.

[ edit ] Implicitly-declared copy assignment operator

If no user-defined copy assignment operators are provided for a class type ( struct , class , or union ), the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T & T :: operator = ( const T & ) if all of the following is true:

  • each direct base B of T has a copy assignment operator whose parameters are B or const B& or const volatile B &
  • each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M& or const volatile M &

Otherwise the implicitly-declared copy assignment operator is declared as T & T :: operator = ( T & ) . (Note that due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument)

A class can have multiple copy assignment operators, e.g. both T & T :: operator = ( const T & ) and T & T :: operator = ( T ) . If some user-defined copy assignment operators are present, the user may still force the generation of the implicitly declared copy assignment operator with the keyword default . (since C++11)

Because the copy assignment operator is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.

[ edit ] Deleted implicitly-declared copy assignment operator

The implicitly-declared or defaulted copy assignment operator for class T is defined as deleted in any of the following is true:

  • T has a non-static data member that is const
  • T has a non-static data member of a reference type.
  • T has a non-static data member that cannot be copy-assigned (has deleted, inaccessible, or ambiguous copy assignment operator)
  • T has direct or virtual base class that cannot be copy-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
  • T has a user-declared move constructor
  • T has a user-declared move assignment operator

[ edit ] Trivial copy assignment operator

The copy assignment operator for class T is trivial if all of the following is true:

  • The operator is not user-provided (meaning, it is implicitly-defined or defaulted), and if it is defaulted, its signature is the same as implicitly-defined
  • T has no virtual member functions
  • T has no virtual base classes
  • The copy assignment operator selected for every direct base of T is trivial
  • The copy assignment operator selected for every non-static class type (or array of class type) memeber of T is trivial

A trivial copy assignment operator makes a copy of the object representation as if by std::memmove . All data types compatible with the C language (POD types) are trivially copy-assignable.

[ edit ] Implicitly-defined copy assignment operator

If the implicitly-declared copy assignment operator is not deleted or trivial, it is defined (that is, a function body is generated and compiled) by the compiler. For union types, the implicitly-defined copy assignment copies the object representation (as by std::memmove ). For non-union class types ( class and struct ), the operator performs member-wise copy assignment of the object's bases and non-static members, in their initialization order, using, using built-in assignment for the scalars and copy assignment operator for class types.

The generation of the implicitly-defined copy assignment operator is deprecated (since C++11) if T has a user-declared destructor or user-declared copy constructor.

[ edit ] Notes

If both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either prvalue such as a nameless temporary or xvalue such as the result of std::move ), and selects the copy assignment if the argument is lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.

[ edit ] Copy and swap

Copy assignment operator can be expressed in terms of copy constructor, destructor, and the swap() member function, if one is provided:

T & T :: operator = ( T arg ) { // copy/move constructor is called to construct arg     swap ( arg ) ;     // resources exchanged between *this and arg     return * this ; }   // destructor is called to release the resources formerly held by *this

For non-throwing swap(), this form provides strong exception guarantee . For rvalue arguments, this form automatically invokes the move constructor, and is sometimes referred to as "unifying assignment operator" (as in, both copy and move).

[ edit ] Example

cppreference.com

Move assignment operator.

A move assignment operator is a non-template non-static member function with the name operator = that can be called with an argument of the same class type and copies the content of the argument, possibly mutating the argument.

[ edit ] Syntax

For the formal move assignment operator syntax, see function declaration . The syntax list below only demonstrates a subset of all valid move assignment operator syntaxes.

[ edit ] Explanation

The move assignment operator is called whenever it is selected by overload resolution , e.g. when an object appears on the left-hand side of an assignment expression, where the right-hand side is an rvalue of the same or implicitly convertible type.

Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, move-assigning from a std::string or from a std::vector may result in the argument being left empty. This is not, however, a guarantee. A move assignment is less, not more restrictively defined than ordinary assignment; where ordinary assignment must leave two copies of data at completion, move assignment is required to leave only one.

[ edit ] Implicitly-declared move assignment operator

If no user-defined move assignment operators are provided for a class type, and all of the following is true:

  • there are no user-declared copy constructors ;
  • there are no user-declared move constructors ;
  • there are no user-declared copy assignment operators ;
  • there is no user-declared destructor ,

then the compiler will declare a move assignment operator as an inline public member of its class with the signature T & T :: operator = ( T && ) .

A class can have multiple move assignment operators, e.g. both T & T :: operator = ( const T && ) and T & T :: operator = ( T && ) . If some user-defined move assignment operators are present, the user may still force the generation of the implicitly declared move assignment operator with the keyword default .

The implicitly-declared (or defaulted on its first declaration) move assignment operator has an exception specification as described in dynamic exception specification (until C++17) noexcept specification (since C++17) .

Because some assignment operator (move or copy) is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.

[ edit ] Implicitly-defined move assignment operator

If the implicitly-declared move assignment operator is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used or needed for constant evaluation (since C++14) .

For union types, the implicitly-defined move assignment operator copies the object representation (as by std::memmove ).

For non-union class types, the move assignment operator performs full member-wise move assignment of the object's direct bases and immediate non-static members, in their declaration order, using built-in assignment for the scalars, memberwise move-assignment for arrays, and move assignment operator for class types (called non-virtually).

As with copy assignment, it is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined move assignment operator:

[ edit ] Deleted move assignment operator

The implicitly-declared or defaulted move assignment operator for class T is defined as deleted if any of the following conditions is satisfied:

  • T has a non-static data member of a const-qualified non-class type (or possibly multi-dimensional array thereof).
  • T has a non-static data member of a reference type.
  • T has a potentially constructed subobject of class type M (or possibly multi-dimensional array thereof) such that the overload resolution as applied to find M 's move assignment operator
  • does not result in a usable candidate, or
  • in the case of the subobject being a variant member , selects a non-trivial function.

A deleted implicitly-declared move assignment operator is ignored by overload resolution .

[ edit ] Trivial move assignment operator

The move assignment operator for class T is trivial if all of the following is true:

  • It is not user-provided (meaning, it is implicitly-defined or defaulted);
  • T has no virtual member functions;
  • T has no virtual base classes;
  • the move assignment operator selected for every direct base of T is trivial;
  • the move assignment operator selected for every non-static class type (or array of class type) member of T is trivial.

A trivial move assignment operator performs the same action as the trivial copy assignment operator, that is, makes a copy of the object representation as if by std::memmove . All data types compatible with the C language (POD types) are trivially move-assignable.

[ edit ] Eligible move assignment operator

Triviality of eligible move assignment operators determines whether the class is a trivially copyable type .

[ edit ] Notes

If 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 lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.

It is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined move assignment operator (same applies to copy assignment ).

See assignment operator overloading for additional detail on the expected behavior of a user-defined move-assignment operator.

[ edit ] Example

[ edit ] defect reports.

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

[ edit ] See also

  • constructor
  • converting constructor
  • copy assignment
  • copy constructor
  • default constructor
  • aggregate initialization
  • constant initialization
  • copy initialization
  • default initialization
  • direct initialization
  • list initialization
  • reference initialization
  • value initialization
  • zero initialization
  • move constructor
  • 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 11 January 2024, at 19:29.
  • This page has been accessed 755,943 times.
  • Privacy policy
  • About cppreference.com
  • Disclaimers

Powered by MediaWiki

Vishal Chovatiya

2 Wrong Way to Learn Copy Assignment Operator in C++ With Example

2-wrong-way-to-learn-copy-assignment-operator-in-c

Implicitly-declared special member functions

While I was introducing myself to C++, I was confused about the syntax of the copy assignment operator in C++ & some of its use-cases. I have learned those lessons the hard way. And so I have decided to write this article, where we see 2 wrong way to learn copy assignment operator in C++ with example. And we also see why we need it & why its syntax like that only. Although I am not an expert or pro but this what I have learned so far from various sources.

  • 1 Why Do We Need Copy Assignment Operator in C++?
  • 2.1 Copy Assignment Operator in C++ With Example
  • 3.1 No. 1: Let’s try return by value
  • 3.2 No. 2: Let’s try return by pointer
  • 4 Conclusion

Why Do We Need Copy Assignment Operator in C++?

  • The simple answer is just to assign data. As we do an assignment in primitive data types like int a; a = 5 . Sometimes we also need to do this in our user-defined data type i.e. class/struct. The answer would be the same as copy constructor which I have given here .
  • Write your own assignment operator that does the deep copy if you are using dynamic memory.
  • Do not allow the assignment of one object to another object or sub-object (object slicing). We can create our own dummy assignment operator and make it private or simply delete it.

Why Do We Need to Return Something From the Copy Assignment Operator?

  • While I was learning about the copy assignment operator, I always had a doubt that why do we need to return value from the copy assignment operator function. Let’s consider following example:

Copy Assignment Operator in C++ With Example

  • Actually, we don’t need to, if you look at the above code we are already assigning to a current object i.e. x2 using this pointer who called copy assignment operator function.
  • I can understand the need for const in the argument of the copy assignment operator function. But the return value was not justifiable to me until I saw the following code:
  • If you make return type of copy assignment operator function as void , the compiler won’t throw error till you are using x2 = x1; .
  • But when assignment chain will be created like x3 = x2 = x1; you have to return something so that it can be an argument on further call to copy assignment operator.
  • So we have to return something from the copy assignment operator to support assignment chaining feature. But what should be the appropriate return type? This will lead us to our next point.

What Should Be the Appropriate Return Type?

I know, you will say we have to return a reference to the current object. Yeh! that’s correct also but why not return by value or pointer ? Ok, then, let’s see 2 wrong way to learn copy assignment operator in C++

No. 1: Let’s try return by value

Note that I have not taken an argument as const in copy assignment operator.

  • When you will compile the above code, GCC will throw an error as follows:
  • Let’s understand all these statements one-by-one
  • Above statement is correct & works fine as we are not utilising return value provided by the copy assignment operator.
  • This statements is perfectly fine & have no problem in a compilation. But Statement 2 is meaningless as we are first assigning x2 into x3 which returns a temporary(AKA anonymous) object which again calls a copy assignment operator with x1 as an argument. This works fine but at the end call of the copy assignment operator, we are assigning the value of x1 to a temporary object which is meaningless.
  • Probable transformation of Statement 2 by the compiler would be
  • With more simplicity
  • As you can see I have taken temp object which usually created by the compiler as we are returning an object by value. So this way output would be 1 2 2 which is not correct.
  • Now we will observe Statement 3
  • Probable transformation of Statement 3 by the compiler would be
  • Code till operation x2 = x1 is fine we have seen it earlier but when the result of that operation becomes an argument to another copy assignment operator function, it will again create the problem of temporary object binding to a non-const reference.
  • If you don’t know about “ temporary object binding to non-const reference ” then you should find out the reason behind why the following program is not working, you will understand everything you wanted to know for Statement 3 .
  • Note that the above code will work in some of the old compilers like VC2012, etc. Now we will move to Statement 4
  • This will also throw the same error as Statement 3 because conceptually both are same. Although Statement 3 & Statement 4 can also be valid if you modify argument of copy assignment operator from pass by reference to pass by value which we know adds the unnecessary overhead of calling copy constructor which also stands true for the return type.

No. 2: Let’s try return by pointer

  • This time we will not observe all four statements rather will go for 2 basic statement which is also valid for primitive data types.
  • Statement 1 is not correct but still works fine. While Statement 4 throws an error
  • Probable transformation of Statement 4 by the compiler would be
  • This will not work simply because of the result of an operation ( x2 = x1 ) is pointer & copy assignment operator function wants a reference as an argument.
  • Now you will say that why we just not change argument with pointer rather than accepting it as a reference. Nice idea! I would say
  • Now to call above copy assignment operator you need to use the following operation
  • Because we are expecting pointer as an argument in copy assignment operator. x1 = x2 or x3 = x2 = x1 won’t work anymore.
  • If you are still getting the correct answer as 1 1 1 in your output window then just consider print from cout . You are getting the correct answer 1 1 1 because default copy constructor provided by the compiler is getting called every time. Technically, we have just overloaded copy constructor by changing its return type & argument as a pointer.
  • Above are the reason why it is not feasible to use pass by value or pointer an argument or return type of copy assignment operator .
  • Compiler designer have designed standard in such a way that your class object operation should also work same as primitive type operations like

assignment operator in C++ with example

Related Articles

21 new features of Modern C++ to use in your project, Move Constructor & Assignment Operator, unique_ptr with example in C++

Move Constructor & Assignment Operator With std::shared_ptr

7 Advance C++ programming styles and idiom examples you should know vishal chovatiya

7 Advance C++ Concepts & Idiom Examples You Should Know

C++ Template | variadic template C++

C++ Template: A Quick UpToDate Look(C++11/14/17/20)

Search code, repositories, users, issues, pull requests...

Provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

to_json / from_json with nested type #1648

@btut

btut commented Jun 21, 2019 • edited

@btut

btut commented Jun 21, 2019

Sorry, something went wrong.

@nickaein

nickaein commented Jun 21, 2019

  • 👍 3 reactions

@nlohmann

nlohmann commented Jun 24, 2019

Btut commented jun 24, 2019.

  • ❤️ 1 reaction

@nickaein

wyfSunflower commented Jun 29, 2022

@gregmarr

gregmarr commented Jun 29, 2022

@falbrechtskirchinger

falbrechtskirchinger commented Jun 29, 2022 • edited

Gregmarr commented jun 29, 2022 • edited, falbrechtskirchinger commented jun 29, 2022.

  • 👍 1 reaction

wyfSunflower commented Jun 29, 2022 • edited

Falbrechtskirchinger commented jun 30, 2022.

No branches or pull requests

@nlohmann

IMAGES

  1. 2 Wrong Way to Learn Copy Assignment Operator in C++ With Example

    (the implicit copy assignment operator) not viable

  2. C++: Constructor, Copy Constructor and Assignment operator

    (the implicit copy assignment operator) not viable

  3. C++ : Would a derived class ever have an implicit copy constructor or

    (the implicit copy assignment operator) not viable

  4. Solved 10. One way to implement the copy assignment operator

    (the implicit copy assignment operator) not viable

  5. PPT

    (the implicit copy assignment operator) not viable

  6. Programming example: Copy assignment operator

    (the implicit copy assignment operator) not viable

VIDEO

  1. Arithmetic

  2. DATALAGRING

  3. Implicit and explicit type assignment

  4. Sledge Build (Rainbow Six Siege)

  5. #004 Lec4

  6. C++ Dersleri 23

COMMENTS

  1. In C++ template copy assignment operator not compatible with

    The first copy assignment operator could be compiled OK with instance = {1, 2}. However, the template version would failed with such error: code.cpp:15:14: error: no viable overloaded '=' instance = {1, 2}; ~~~~~~~~ ^ ~~~~~~ code.cpp:3:7: note: candidate function (the implicit copy assignment operator) not viable: cannot convert initializer ...

  2. Copy assignment operator

    Triviality of eligible copy 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 ...

  3. Copy constructors

    Trivial copy constructor. The copy constructor for class T is trivial if all of the following are true: . it is not user-provided (that is, it is implicitly-defined or defaulted); T has no virtual member functions; ; T has no virtual base classes; ; the copy constructor selected for every direct base of T is trivial; ; the copy constructor selected for every non-static class type (or array of ...

  4. Copy assignment operator

    The copy assignment operator selected for every non-static class type (or array of class type) memeber of T is trivial. A trivial copy assignment operator makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially copy-assignable.

  5. Copy Assignment Operator

    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.. Implicitly-declared copy assignment operator. If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class.

  6. How to copy assign volatile data with std::array?

    Basically, std::array doesn't have a volatile -aware copy assignment operator. Actually, it's even worse than that, since none of its members are volatile. Like begin() and end(), which would allow for using std::copy(). Not even operator[](), which means you can't even write a loop and do it manually.

  7. Mead's Guide to Modern C++

    error: no viable overloaded '=' f = 43; // The conversion constructor is called implicitly ~ ^ ~~ note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'int' to 'const Foo3' for 1st argument class Foo3 ^ error: no matching function for call to 'fn1' fn1(43); // The conversion constructor is called ...

  8. implicit copy assignment operator

    George wrote: I know [that] the compiler will generate a copy assignment operator (among other functions) if you don't provide one in your class declaration. No. For class X, yhe compiler will supply 1. an assignment operator, X& X::operator= (const X&); 2. a *default* constructor X::X (void); 3. ans a copy constructor X::X (const X&); by ...

  9. 0-dim local accessor · Issue #9418 · intel/llvm · GitHub

    note: candidate function (the implicit copy assignment operator) not viable: 'this' argument has type 'const sycl::local_accessor<int, 0>', but method is not marked const class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(local_accessor) local_accessor.

  10. candidate function not viable:

    The issue is in how you call the function, not how you declare it. > requires 2 arguments, but 0 were provided. You tried to call makecube(); You should have, for example, if you're calling directly from main

  11. class with a rvalue reference in the copy constructor fails to compile

    yurivict changed the title class with rvalue reference in copy constructor fails to compile class with the rvalue reference in the copy constructor fails to compile Dec 23, 2022 EugeneZelenko added c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" and removed new issue labels Dec 24, 2022

  12. Copy assignment operator

    Implicitly-declared copy assignment operator. If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T & T:: operator = (const T &) if all of the following is ...

  13. no viable overloaded '=' when assigning a map with non-move/copy

    In foo2 the move/copy constructors seem both fine so I don't get it why it failed to assign the value of a map. The text was updated successfully, but these errors were encountered: All reactions

  14. Move assignment operator

    The move assignment operator is called whenever it is selected by overload resolution, e.g. when an object appears on the left-hand side of an assignment expression, where the right-hand side is an rvalue of the same or implicitly convertible type.. Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors ...

  15. 2 Wrong Way to Learn Copy Assignment Operator in C++ With Example

    X operator = (X &rhs) {. ^. Let's understand all these statements one-by-one. x2 = x1; // Statement 1: Works fine. Above statement is correct & works fine as we are not utilising return value provided by the copy assignment operator. (x3 = x2) = x1; // Statement 2: Correct, but meaningless statement.

  16. Const error for this or no viable overload

    I guess this is caused by my overloads of = operator. But I don't know why, even after I simplify the code for 9 times. ... note: candidate function (the implicit copy assignment operator) not viable: 'this' argument has type 'const A<int>', but method is not marked const class A { ^ test9.cpp:8:10: note: candidate function not viable: 'this ...

  17. no viable overloaded '=' · Issue #17 · avilleret/pix_opencv · GitHub

    I tried to see if there's an operator overload pix_opencv_motempl.h file, I see this class inherits from the GemPixObj which is included with #inlcude "Base/GemPixObj.h". Maybe the file can't access gem properly?... maybe this is the problem.

  18. c++

    Now, for the issue. I'm trying to store the proper ticket variable (either t1 or t2) into t3 so I can return that to the main. When I use "=" to set either t1 to t3, it says "no viable overloaded '='". The following is my code: ticket(); double input(); double output(); friend ticket operator *(const ticket &t1, const ticket &t2);

  19. to_json / from_json with nested type · Issue #1648

    Copy link btut commented Jun 21, 2019 • ... error: no viable overloaded '=' AppConfig = ConfigFile; ./configuration.h:13:8: note: candidate function (the implicit move assignment operator) not viable: no known conversion from 'std::ifstream' (aka 'basic_ifstream<char>') to 'AppConfiguration' for 1st argument struct AppConfiguration ...