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.

Posted 2018-05-09