C++ Universal Reference and Perfect Forwarding
Introduction
Suppose we have $m$ classes and each class has $n$ constructors which consumes one single parameter, without using template (due to there is no universal way for implementation), how many constructors should we implement in total? The answer is obvious. We need to implement $mn$ constructors in total. The complexity is quadratic and this is normal in modern programming.
An example of the implementation for class A
which contains 2 constructors could be
1 | struct A { |
Suppose we have a new class which has $m$ member variables, each of the member variables is a class of the $m$ classes we have declared above, if we want to implement every combination of the possible constructors, without using template, at least how many constructors should we implement in total? The answer is not difficult either. We need to implement $n^m$ constructors in total. The complexity becomes exponential and when $m$ is large we will just not affording implementing.
An example of the implementation for class B
which contains 3 member variables could be
1 | class B { |
There are $2^3 = 8$ implementations for the constructors in total and the implementation for this class already looks very “heavy”.
These are combinatorial implementation could be extremely simplified by universal reference and perfect forwarding.
Universal Reference and Perfect Forwarding
Universal Reference
The “universal reference”, proposed by Scott Meyers, alleviated us from this combinatorial mathematics.
Instead of creating $n^m$ constructors, we would only create $m$ constructors using universal reference!
An example of the equivalent implementation for class B
could be
1 | class B { |
I have also touched some bases of the universal reference rules in my previous blog post “C++ Reference Collapsing”.
Perfect Forwarding
Suppose we want to create a wrapper function which returns a std::unique_ptr<T>
for every single possible class T
. Such wrapper function could just be the C++ standard library function std::make_unique
.
Because there could be infinitely number of classes and the number of arguments for the functions could all be different, even with the universal reference mentioned above, it is impossible to cover all the use cases.
For example, we will have to implement
1 | template<class T, class U1> |
This will go endless.
Fortunately, perfect forwarding resolves this issue. Instead of implementing infinite number of wrapper functions, we only need to create one.
1 | template<class T, class... U> |
Example
A full example is copied from the std::forward webpage on CPP Reference.
1 |
|
References
C++ Universal Reference and Perfect Forwarding
https://leimao.github.io/blog/Universal-Reference-Perfect-Forwarding/