c++typedefusingalias-declaration

Does the guidance against using in headers apply to type aliases?


I'm aware of the guidance to not use using in C++ header files, the reasons for which have been widely discussed, e.g. here.

I was looking for a more C++ alternative to typedef when I came to this article describing "alias declaration."

Now, just because concepts are getting jumbled in my head, I'm unclear whether the guidance against using using in headers is specifically for this way of using it:

// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

using namespace std;

#endif

...or does it also apply to alias declarations?

I want to use typedef/alias declarations/something functionally equivalent because of this situation:

// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <tuple>

// This function's signature is getting cumbersome
int func(const std::tuple<a::really::long_namespaced::thing, a::really::long_namespaced::thing, a::really::long_namespaced::thing>& tup = {1, 2, 3});

#endif

Basically, a function signature is unwieldy because how long the text of its arguments is.

What's a generally accepted way of making this more readable if using for an alias declaration is discouraged in header files?


Solution

  • using keyword has a few meanings:

    1. using-directives
      using-directive in the form using namespace X; brings all the names from namespace X into current namespace. This is very bad in header files, since every user of this header will risk name conflicts with any name that exists in X. Especially namespace std has a lot of very common names, so you'd force users of your header to avoid a lot of good names. It also makes code brittle, since simply adding another header in header with using namespace can break code in opposite corner of repository that happens to use one of the names already.
    2. using-declarations
      using-declarations in the form of using X::Y; bring just one name Y from namespace X into current namespace. For example, using std::string; allows to use string as a name, but vector still requires std::. This still can cause confusion as to what this type really is, but at least it doesn't bring all the names in.
    3. Type alias
      Type alias in the form using A = X::Y; introduces a new name into your namespace. Instead of std::tuple<a::really::long_namespaced::thing, a::really::long_namespaced::thing, a::really::long_namespaced::thing> you can provide a new name that describes the meaning of this type better. In my opinion, this would be preferable in your case (std::tuple doesn't convey a lot of meaning, a well chosen name would make it easier to read, type and comprehend).

    Additionally, you can alias namespaces as well. namespace shortName = a::really::long_namespaced; can solve an issue with long namespace used often in your code. A common example from standard library is namespace fs = std::filesystem to allow writing e.g. fs::path instead of std::filesystem::path. This is better than 2. IMHO, because you keep some reference to where the name is from.