samedi 24 février 2018

Conditionally remove methods in template class

Background: The usage of STL makes builds slow due to template code bloat: quite often the same methods are independently instantiated in many different translation units, being compiled and optimized many times. One way to avoid this object code duplication for template classes is to use explicit template instantiation and extern template declaration, but STL implementations do not support them. I'm trying to implement an equivalent of std::vector<T> which would support explicit instantiations.


Question: I have a template class vector<T>, and I want some of its methods to be removed if the template argument T does not satisfy some condition. To make things worse, here are additional requirements:

  1. It must be possible to explicitly instantiate vector<T>, regardless if whether T satisfies the condition or not.
  2. When someone calls a conditionally removed method, a compilation error should be emitted.
  3. I have several such conditions, and a set of methods dependent on each of them.

Discussion: For example, the method vector<T>::push_back(const T&) cannot work when T is not copy-constructible.

If I leave this method implementation intact, then compiler will produce error if I explicitly instantiate e.g. vector<unique_ptr<int>>, because it won't be able to copy unique_ptr<int>. This contradicts requirement 1, and makes the desired code bloat optimization impossible.

I can make two different implementations of vector<T>::push_back(const T&), one for copy-constructible types and one for the others. This can be done using SFINAE overloading or with a helper template class. The implementation for non-copy-constructible case can simply throw exception or call terminate. But then calling the method push_back for vector<unique_ptr<int>> will only crash during runtime. It won't generate compile error, as said in requirement 2.

Finally, there are several conditions, each of them rules out some methods. The type T can lack the following properties in various combinations: copy-constructible, copy-assignable, default-constructible, move-constructible. The solution from the related question seems to work for one condition only, which does not satisfy requirement 3.

The only idea I have left is to use some sort of partial specialization of the whole vector<T> combined with preprocessor hacks. But this would need something like 16 separate specializations, which sounds terrible.

P.S. It becomes pretty obvious to me that STL design is inherently dependent on the mechanics of implicit instantiation, which makes it very hard to reduce the code bloat caused by them.

Aucun commentaire:

Enregistrer un commentaire