It's actually a general question about interface design, but it's easier for me to just take std::pair
as an example:
template <class T1, class T2>
struct pair {
...
pair(const T1& x, const T2& y);
template<class U, class V> pair(U&& x, V&& y);
...
};
So we can see there are two overloads that both take 2 arguments to initialize the 2 members of the pair. My question is, what is the benefit of providing the first one while the second one is available? Are there any types of arguments that can only be passed to the first one but not the second one?
(Let's just put aside the standard library's consideration for backward compatibility for a while, and discuss the interface design as a general question.)
Sample implementation
template<typename T1, typename T2>
struct simple_pair {
simple_pair (T1 const& v1, T2 const& v2) // (1)
: first (v1)
, second (v2)
{ }
template<class U, class V>
simple_pair (U&& v1, V&& v2) // (2)
: first (std::forward<U> (v1))
, second (std::forward<V> (v2))
{ }
T1 first;
T2 second;
};
Even though it might seem superfluous to provide both overload (1)
and (2)
there are cases where the second isn't usable, and the first one is not only preferred but actually required.
Consider that we'd like to construct some or both of our values while passing them to the constructor of simple_pair
, without the first overload we would explicitly have to specify at least one of the types a second time.
T val;
simple_pair<T, U> p1 { {}, {} }; // only (1) is applicable
simple_pair<T, U> p2 { val, {} }; // only (1) is applicable
simple_pair<T, U> p3 { T {}, U {} }; // can use (1) and (2), but this require a lot of typing
Alternative implementation
If we instead had an implemenation using something as the below we could get around the "superfluous" overloads since the compiler would then know what type we'd like to construct in cases where such information is required.
template<typename T1, typename T2>
struct simple_pair {
template<class U = T1, class V = T2>
simple_pair (U&& v1, V&& v2)
: first (std::forward<U> (v1))
, second (std::forward<V> (v2))
{ }
T1 first;
T2 second;
};
T val;
simple_pair<T, U> p1 { {}, {} }; // legal
simple_pair<T, U> p2 { val, {} }; // legal
simple_pair<T, U> p3 { T {}, U {} }; // legal
Why isn't std::pair
stated to be implemented using the alternative implementation?
We can only guess, but presumably it's because of backward compatibility and the fact that specifying it the way it currently stands ease1 implementation for library implementors.
By having two separate overload one can easily disable the template<class U, class V> simple_pair (U&&, V&&)
overload by conditionally adding it using macros (to see if we are using c++11
(or later)), instead of conditionally opting it out and adding another one.
Further potential reasons
Removing something from the standard is always a delicate thing to do.. following the better safe than sorry idiom; "if it doesn't hurt, leave it in." - @PlasmaHH
Everyone knows that the more lines of code you write, the better programmer you are.. and the better programmer you are; the more you get payed.
1. surely not by much, but heck.. it doesn't hurt being a bit pedantic.. ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With