0

Let's say I'm writing a trait, and I want to provide out-of-the-box specializations for all the standard library containers.

template <class T>
struct supports_fast_insertion: std::false_type {};

template <class T, class A>
struct supports_fast_insertion<std::list<T, A>>: std::true_type {};

template <class T, class A>
struct supports_fast_insertion<std::forward_list<T, A>>: std::true_type {};

template <class T, class A>
struct supports_fast_insertion<std::unordered_set<T, A>>: std::true_type {};

// ...

This requires #include <list>, #include <forward_list>, #include <unordered_set>, and so on. That means that any file that includes my type trait will also include half of the containers library, which seems sloppy.

If I were only specializing for user-defined types, I could just forward-declare all the ones I care about. But adding declarations to namespace std – even mere forward declarations – invokes undefined behavior.

Is there a way to avoid the #include explosion? Is there a conventional way for libraries to handle this?

Maxpm
  • 24,113
  • 33
  • 111
  • 170
  • Did you consider providing one header for each specialisation? – Yunnosch Mar 24 '19 at 06:55
  • It won't work because to be able to use `std::list`, you'll have to provide a forward declaration of the template. As you know, those forward declarations are tricky, specially with all the default arguments. – R Sahu Mar 24 '19 at 06:56
  • @Yunnosch Segregating specializations in individual headers seems dangerous, because forgetting to include a header could silently change the meaning of the program. – Maxpm Mar 24 '19 at 06:58
  • 2
    @Maxpm, you can avoid the pitfall if there is no default implementation. i.e. you only allow use of specializations. – R Sahu Mar 24 '19 at 07:00
  • 1
    @RSahu - That's throwing the baby out with the bathwater. The default implementation is what makes the trait, well, a trait. You can't use it in a SFINAE context without a default. – StoryTeller - Unslander Monica Mar 24 '19 at 07:29
  • @StoryTeller, you have a point. – R Sahu Mar 24 '19 at 07:32
  • @Maxpm What's the criteria for 'supporting fast insertion'? Maybe you could determine that in your trait by detecting presence of a specific members in `T`? – HolyBlackCat Mar 24 '19 at 07:40

1 Answers1

3

Is there a way to avoid the #include explosion? Is there a conventional way for libraries to handle this?

Sadly, there isn't a satisfactory answer as of the time of writing this. If you wish to avoid undefined behavior at all costs, then you have to bite the bullet and include those headers.

If you know your intended implementations, and don't mind the fact it's formally non-standard, you can create an internal header (to your library) that does the forwarding under a zillion compiler and library specific macro checks. Boost is an example of a library that does this, see boost/detail/container_fwd.hpp.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458