January 17th, 2022

C# and C++ type aliases and their consequences

The C# and C++ language provide ways to introduce shorter names for things. These shortcuts do not have their own identities; they merely let one name be used as a shorthand for the other thing.

// C#
using Console = System.Console;

// C++
using Project = Contoso::Project;

The C# and C++ programming languages call these aliases. You are allowing an existing type to go by a different name. It does not create a new type, and the new name is interchangeable with the old one.

// C++
extern void UpdateProject(Contoso::Project& project);

void example()
{
    Project project;
    UpdateProject(project); // this works
}

Similarly, when you import a namespace with a using directive, the names from the other namespace are visible in your namespace, but they still belong to that other namespace.¹

// C++
namespace Other
{
    struct OtherStruct;
}

namespace Mine
{
    using namespace Other;
}

void Welcome(Mine::OtherStruct s);

The signature of the Welcome function is void Welcome(Other::OtherStruct), not void Welcome(Mine::OtherStruct).

This trick also gives you a way to switch easily between two options:

#ifdef USE_CONTOSO_WIDGET
using Widget = Contoso::Widget;
#else
using Widget = LitWare::Widget;
#endif

// code that uses Widget without caring whose widget it is

The fact that these aliases do not introduce new types means that when you go looking in the debugger, you will see the symbols decorated with their original names. Which can be both a good thing and a bad thing.

It’s a good thing if you want the original name to be the one seen by the outside world. For example, you might create aliases for commonly-used types in your component, but you want people outside your component to use the original names.

// component.h

namespace Component
{
    struct ReversibleWidget;

    void CheckPolarity(ReversibleWidget const&);
}

// component.cpp (implementation)
#include<component.h>

using FlipWidget = Component::ReversibleWidget;

void Component::CheckPolarity(FlipWidget const& widget)
{
    ... do stuff ...
}

Inside your component, you’d rather just call it a FlipWidget, because that was the internal code name when the product was being developed, and then later, management decided that its public name should be ReversibleWidget. You can create an alias that lets you continue using your internal code name, so you don’t have to perform a massive search-and-replace across the entire code base (and deal with all the merge conflicts that will inevitably arise).

That the symbols are decorated with the original names can be a bad thing if the original name is an unwieldy mess, which is unfortunately the case with many classes in the C++ standard library.

In the C++ standard library, string is an alias for basic_string<char, std::char_traits<char>, std::allocator<char> >,² so a function like

void FillLookupTable(std::map<std::string, std::string>& table);

formally has the signature (deep breath)

FillLookupTable(std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&):

Good luck typing that into a debugger.

¹ The fact that they remain in the original namespace has consequences for argument-dependent lookup:

namespace X
{
    struct S {};
    void fiddle(S const&);
}

namespace Y
{
    using namespace X;
    void fiddle(S const&);
}

void test()
{
    Y::S s;
    fiddle(s); // X::fiddle, not Y::fiddle
}

² What you’re seeing is a combination of the type alias and the template default parameters.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

4 comments

Discussion is closed. Login to edit/delete existing comments.

  • Huo Yaoyuan · Edited

    The alias in C# works somehow differently with C++. For example,
    <code>

    You can't refer it as <code>. <code> in C# always imports names into the "un-namespaced" scope.
    And also, it only import top level things, not nested namespaces. With <code>, you still can't refer <code> as <code>.

    The differences between the two languages can be an interesting topic.

    <code> in C++ powers templates by associating two types together.

    Read more
  • Igor Tandetnik

    It’s somewhat confusing when you refer to types as “objects”. At least in C++, “object” is a term of art, and types are not objects. You might have meant “entity”; types are entities (and so are objects), while type aliases are not.

    • Mystery Man

      I concur. At least in .NET, System.Console is a class, not object.