Hiding template parameters from casual users
Related to Bob Steagall’s C++Now 2018 talk on improving the STL containers…
Bob suggested that it might have been better to split up the newbie-friendly std::vector<T>
from the most customizable
and power-user-friendly std::vector<T, A>
. And even more so: splitting up std::unordered_map<K, V>
from the
power-user-friendly std::unordered_map<K, V, H, E, A>
.
Notice that I can already write std::vector<T>
in my non-power-user code. But there’s a bit of a downside there
in terms of documentation (the defaulted A
parameter has to be documented anyway) and compiler error messages
(they’ll probably include the value of the A
parameter, cluttering up my error output, which is probably the worst
part of C++ for newbies).
I can think of three different approaches to “hiding” a template type parameter such as A
.
First, what the STL does: The only class is the expert-friendly class. It has defaulted parameters, so that the “newbie-friendly” version just omits those parameters.
template<class T, class A = std::allocator<T>>
class vector {
// ...
};
vector<int> v1;
vector<int, OtherAlloc> v2;
Second, we could do something inspired by std::pmr
: The expert-friendly class has no
defaulted parameters, but we provide a newbie-friendly type alias.
template<class T, class A>
class expert_vector {
// ...
};
template<class T>
using vector = expert_vector<T, std::allocator<T>>;
vector<int> v1;
expert_vector<int, OtherAlloc> v2;
Third, we could implement both “newbie-friendly” and “expert-friendly” versions
as totally different types. In this case we introduce an internal
__basic_vector
to hold the common implementation bits.
template<class T, class A>
class __basic_vector {
// ...
};
template<class T, class A>
class expert_vector : private __basic_vector<T, A> {
// ...
};
template<class T>
class vector : private __basic_vector<T, std::allocator<T>> {
// ...
};
The third approach is the only one that really solves the “confusing error messages”
problem for newbies. But it’s also the only one that causes an API-reusability problem:
our newbies with vector<T>
objects can no longer pass those objects to expert-written
APIs that expect expert_vector<T, A>
objects.
No particular conclusion here. I’m fairly sure I prefer the second approach, because it doesn’t rely on defaulted parameters (which are the devil) and it doesn’t cause API problems as the third approach does.