what does `using std::swap` inside the body of a class method implementation mean?
This mechanism is normally used in templated code, i.e. template <typename Value> class Foo
.
Now the question is which swap to use. std::swap<Value>
will work, but it might not be ideal. There's a good chance that there's a better overload of swap
for type Value
, but in which namespace would that be? It's almost certainly not in std::
(since that's illegal), but quite likely in the namespace of Value
. Likely, but far from certain.
In that case, swap(myValue, anotherValue)
will get you the "best" swap possible. Argument Dependent Lookup will find any swap in the namespace where Value
came from. Otherwise the using
directive kicks in, and std::swap<Value>
will be instantiated and used.
In your code, mSize
is likely an integral type, and mArray
a pointer. Neither has an associated namespace, and std::swap
is with 99.9% certainty optimal for them anyway. Therefore, the using std::swap;
declaration seems useless here.
How does using std::swap enable ADL?
The "enable ADL" comment applies to the transformation of
std::swap(first.mSize, second.mSize);
std::swap(first.mArray, second.mArray);
to
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
You're right, ADL only requires an unqualified name, but this is how the code is re-worked to use an unqualified name.
Just plain
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
wouldn't work, because for many types, ADL won't find std::swap
, and no other usable swap
implementation is in scope.
What's the difference between std::ranges::swap() and std::swap()?
In rough terms: std::ranges::swap
generalizes std::swap
.
- If
T
andU
are the same, then it's equivalent to invokingstd::swap
. - Otherwise it will swap the arguments as two ranges - if they're ranges.
- Otherwise it will perform a swap-like procedure with move assignment between the different types T and U.
- If even that can't happen, it will fail to compile.
See the cppreference page for details.
I must say I find this function to be somewhat confusing and baroque, but then - maybe people more experienced with the range library develop an intuition as to why it makes sense.
The c++ using statement within a function, followed by a function name (for ADL?)
The using statement makes this line work:
swap(first, second);
Notice that we can omit std::
in front of swap
.
The important thing there is that std::swap(...)
is a qualified lookup, but swap(...)
is an unqualified lookup. The main difference is that qualified lookup is calling a function in a specific namespace or scope (the one specified), whereas unqualified lookup is a bit more flexible since it will look into parent scope of the current context and also the global namespace. In addition, unqualified lookup will also look into the scope of the type of the arguments. It's a nice tool, but also dangerous since it can call function from unexpected places.
ADL will only work with unqualified lookup, since it has to search for other namespaces and scopes.
The using std::swap
also ensure that if no function is found through ADL, it will call std::swap
by default.
This idiom allow for user defined swap functions:
struct MyType {
// Function found only through ADL
friend void swap(MyType& l, MyType& r) {
// ...
}
};
Use std::swap between vectors or vector::swap?
You should not use std::swap()
directly in any case! Instead, you should use something like this:
using std::swap;
swap(x, y);
For std::vector<...>
it probably doesn't make a difference as std::vector<...>
obviously lives in namespace std
. Otherwise the key difference is that with using std::swap()
the default implementation is being used while with the approach outlined about ADL can find a better version.
Using swap(x, y)
for std::vector<...>
s x
and y
will just call x.swap(y)
. For consistency with other uses I would use the approach listed above.
References:
- How does "using std::swap" enable ADL?
- what does `using std::swap` inside the body of a class method implementation mean?
public friend swap member function
There are several ways to write swap
, some better than others. Over time, though, it was found a single definition works best. Let's consider how we might think about writing a swap
function.
We first see that containers like std::vector<>
have a single-argument member function swap
, such as:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Naturally, then, our class should too, right? Well, not really. The standard library has all sorts of unnecessary things, and a member swap
is one of them. Why? Let's go on.
What we should do is identify what's canonical, and what our class needs to do to work with it. And the canonical method of swapping is with std::swap
. This is why member functions aren't useful: they aren't how we should swap things, in general, and have no bearing on the behavior of std::swap
.
Well then, to make std::swap
work we should provide (and std::vector<>
should have provided) a specialization of std::swap
, right?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
Well that would certainly work in this case, but it has a glaring problem: function specializations cannot be partial. That is, we cannot specialize template classes with this, only particular instantiations:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
This method works some of the time, but not all of the time. There must be a better way.
There is! We can use a friend
function, and find it through ADL:
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
When we want to swap something, we associate† std::swap
and then make an unqualified call:
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
What is a friend
function? There is confusion around this area.
Before C++ was standardized, friend
functions did something called "friend name injection", where the code behaved as if if the function had been written in the surrounding namespace. For example, these were equivalent pre-standard:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
However, when ADL was invented this was removed. The friend
function could then only be found via ADL; if you wanted it as a free function, it needed to be declared as so (see this, for example). But lo! There was a problem.
If you just use std::swap(x, y)
, your overload will never be found, because you've explicitly said "look in std
, and nowhere else"! This is why some people suggested writing two functions: one as a function to be found via ADL, and the other to handle explicit std::
qualifications.
But like we saw, this can't work in all cases, and we end up with an ugly mess. Instead, idiomatic swapping went the other route: instead of making it the classes' job to provide std::swap
, it's the swappers' job to make sure they don't use qualified swap
, like above. And this tends to work pretty well, as long as people know about it. But therein lies the problem: it's unintuitive to need to use an unqualified call!
To make this easier, some libraries like Boost provided the function boost::swap
, which just does an unqualified call to swap
, with std::swap
as an associated namespace. This helps make things succinct again, but it's still a bummer.
Note that there is no change in C++11 to the behavior of std::swap
, which I and others mistakenly thought would be the case. If you were bit by this, read here.
In short: the member function is just noise, the specialization is ugly and incomplete, but the friend
function is complete and works. And when you swap, either use boost::swap
or an unqualified swap
with std::swap
associated.
†Informally, a name is associated if it will be considered during a function call. For the details, read §3.4.2. In this case, std::swap
normally isn't considered; but we can associate it (add it to the set of overloads considered by unqualified swap
), allowing it to be found.
Providing swap() for a C++ template class breaks std::swap()?
The approach taken by STL containers uses a member function and then overload the static function. For example:
template<class T, class Alloc=std::allocator<T> >
class vector
{
T *data;
size_t n;
size_t max_n;
public:
void swap(vector<T, Alloc> &other)
{
swap(this->data, other.data);
swap(this->n, other.n);
swap(this->max_n, other.max_n);
}
};
template<class T, class A>
void swap(vector<T, A> &lhs, vector<T, A> &rhs)
{
lhs.swap(rhs);
}
In the suggested Matrix class, simply take the same approach...
namespace my_space
{
template<typename T>
class Matrix
{
unsigned width_;
unsigned height_;
std::vector<T> data_;
public:
void swap(Matrix<T> &other)
{
std::swap(this->width_, other.width_);
std::swap(this->height_, other.height_);
std::swap(this->data_, other.data_); // calls this->data_.swap(other.data_);
}
};
}
namespace std
{
template<typename T>
void swap(my_space::Matrix<T> &lhs, my_space::Matrix<T> &rhs)
{
lhs.swap(rhs);
}
}
copy and swap idiom with pure virtual class
As your compiler informs you, you cannot create a variable of abstract type. There is no way of dancing around that.
This leaves you three main options:
Stop using pure virtual functions
First, you could just get rid of the pure virtual methods and provide a little stub in each of them that calls std::terminate
, which would obviously break compile time detection of whether all (former) pure virtual methods are overridden in all derived classes.
This will cause slicing, since it will only copy the base class and everything that makes out the derived class is lost.
Use a stub class w/o pure virtual functions
Similar to that, you could create a derived class that implements all virtual methods with simple stubs (possibly calling std::terminate
), and is used only used as a "instantiatable version of the base class".
The most important part to implement for this class would be a constructor that takes a const reference to the base class, so you can just use it instead of copying the base class. This example also adds a move constructor, because I am a performance fetishist.
This causes the same slicing problem as the first option. This may be your intended result, based on what you are doing.
struct InstantiatableA : public A {
InstantiatableA(A const& rhs) : A(rhs) { }
InstantiatableA(A&& rhs) : A(::std::move(rhs)) { }
void print(ostream&) override { ::std::terminate(); }
};
A& A::operator=(InstantiatableA rhs) {
using ::std::swap;
swap(*this, rhs);
return *this;
}
Note: This is really a variable of type A
, although I said it could not be done. The only thing you have to be aware is that the variable of type A
lives inside a variable of type InstantiatableA
!
Use a copy factory
Finally, you can add a virtual A* copy() = 0;
to the base class. Your derived class B
will then have to implement it as A* copy() override { return new B(*this); }
. The reason dynamic memory is necessary is because your derived types may require arbitrarily more memory than your base class.
Is this a proper implementation of the Rule of Five (or Rule of Four and 1/2)?
Most important of all:
- This class doesn't need custom copy/move operations nor the destructor, so rule of 0 should be followed.
Other things:
I don't like
swap(*this, other);
in the move ctor. It forces members to be default-constructed and then assigned. A better alternative would be to use a member initializer list, withstd::exchange
.If initializing all members gets tedious, wrap them in a structure. It makes writing the
swap
easier too.Copy constructor must take the parameter by a const reference.
unique_ptrs which can't be copied. So we move it.
is a bad rationale. If your members can't be copied, don't define copy operations. In presence of custom move operations, the copy operations will not be generated automaticallyMove operations (including the by-value assignment) should be
noexcept
, because standard containers won't use them otherwise in some scenarios.SomeClass() = default;
causes members that are normally uninitialized (int m_int;
) to sometimes be zeroed, depending on how the class is constructed. (E.g.SomeClass x{};
zeroes it, butSomeClass x;
doesn't.)Unless you want this behavior, the constructor should be replaced with
SomeClass() {}
, andm_int
should probably be zeroed (in class body).
Related Topics
What Can Make C++ Rtti Undesirable to Use
Recursive Lambda Functions in C++14
Converting Between C++ Std::Vector and C Array Without Copying
Safely Override C++ Virtual Functions
How Much Overhead Is There in Calling a Function in C++
Why Is Std::Vector::Operator[] 5 to 10 Times Faster Than Std::Vector::At()
Dead Code Identification (C++)
Reinterpret_Cast Between Char* and Std::Uint8_T* - Safe
Optimizations for Pow() with Const Non-Integer Exponent
Interpolate from One Color to Another
When to Use 'Asio_Handler_Invoke'
What Are Potential Dangers When Using Boost::Shared_Ptr
How to Detect Region of Large # of White Pixels Using Opencv
C++11 Type Trait to Differentiate Between Enum Class and Regular Enum
Serialize and Send a Data Structure Using Boost
Macro/Keyword Which Can Be Used to Print Out Method Name
Opencv Orb Not Finding Matches Once Rotation/Scale Invariances Are Introduced
Authenticating Users Using Active Directory in Client-Server Application