I'll skip std::function
and focus on std::any
.
The standard specifies that most overloads of its operator=
must act as if doing a swap operation. For example,
any& operator=( const any& rhs );
Assigns by copying the state of rhs, as if by any(rhs).swap(*this)
see the C++ Reference explanation at https://en.cppreference.com/w/cpp/utility/any/operator%3D.
In this way the standard suggests to the implementers a possible implementation. The question is now: why's that? The answer seems to lie in the type requirements for the template parameter of std::any
:
The class any
describes a type-safe container for single values of any copy constructible type.
.
The property of being copy-constructible is one of the most fundamental an common properties in C++. It does not even require that values of that type be assignable! See: https://en.cppreference.com/w/cpp/concepts/copy_constructible for a detailed explanation of this property. Thus, std::any::operator=
must not rely on the value assignment operator to exist, though it may use it, if it is available. It only invokes a member function swap
of std::any
, which is always defined.
To sum all this up, C++ tries its standard library to be as general as possible, and this is why it uses swaps for the operations you're asking about.
Finally, a simple program that uses std::any
with a type that is not assignable, and still std::any<X>::operator=
compiles and works:
#include <any>
struct X
{
X& operator=(const X&) = delete;
};
int main()
{
X x1;
X x2;
std::any a = x1;
std::any b = x2;
a = b;
}
Edit
Here's my attempt to check if gcc uses operator=
, or whether the property of "copy-constructible" is used throughout the implementation:
#include <any>
#include <iostream>
struct X
{
X& operator=(const X&)
{
std::cout << "op =\n";
return *this;
}
X& operator=(const X&&)
{
std::cout << "move op =\n";
return *this;
}
X(X&&)
{
std::cout << "(&&)\n";
};
X(const X&)
{
std::cout << "(&)\n";
}
X()
{
std::cout << "()\n";
}
// int tab[1000];
};
int main()
{
X x1;
X x2;
std::any a (x1);
std::any b = x2;
std::any c = 0;
a = b;
std::cout << sizeof(a) << "\n";
std::cout << sizeof(c) << "\n";
}
This yields
()
()
(&)
(&)
(&)
16
16
irrespective of whether I uncomment the // int tab[1000];
line or not (to play with the small-size object optimization). This kinda shows the implementation of std:any
needs not operator=
of the value type in the implementation of its own operator=
.