Search code examples
c++scopeusing-directivestype-alias

Where to put using directives in C++ header files


For my project I am using some pretty convoluted data structures, e.g.

std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>

for which I would like to declare type aliases for readability. The code on which I built my project on already did this by putting using statements globally in the header files:

// bar.h
#ifndef BAR_H
#define BAR_H

#include <unordered_map>
#include <list>
#include <memory>
#include "foo.h"

using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;

class Bar {
    FooTable create_foo();
};

#endif

Since my C++ knowledge was a little rusty, I just adopted this style -- but now I read that using using in that way can be problematic, since it forces this alias on everything that includes this header.

Despite a good amount of googling, I could not find a concrete answer on how to handle this properly, only lots of statements on what not to do. So, I just put the using inside the class:

// bar.h
#ifndef BAR_H
#define BAR_H

#include <unordered_map>
#include <list>
#include <memory>
#include "foo.h"


class Bar {
    using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;

    FooTable create_foo();
};

#endif

However this has the drawback, that I need to restate the alias in the source file:

// bar.cpp
#include "bar.h"

using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;

FooTable Bar::create_foo()
{
...
}

While this seems to work, I am not sure whether this is safe... and my gut tells me it is kind of ugly. So before I rewrite my whole project like this I thought I'd ask: Is a there a better/more elegant/safer way to do this? Or should I just refrain from using type aliases in header files completely?


Solution

  • However this has the drawback, that I need to restate the alias in the source file:

    That is incorrect. You need only make it public then specify the proper scope, so you'd call it Bar::FooTable outside of the scope of Bar (which includes return types, unless trailing!):

    Bar::FooTable Bar::create_foo()
    { /* ... */ }
    

    or

    auto Bar::create_foo() -> FooTable
    { /* ... */ }
    

    (Just FooTable is fine within the definition, as it's a member!)

    Your approach is fine, though I'd put everything in a namespace too. Then it doesn't really matter whether your alias is in the class or not: it's still self-contained within your own code. It becomes purely a matter of style, with little to no impact on anyone else.