c++ Type confusion stopper

C++ is hard. But, you are advancing.  Things are becoming clearer and clearer.

By now, you feel confident enough to tackle head-on, even the c++ meta-programming, perhaps? Compile-time shenanigans and the rest?

(for the clever ones, Wandbox is here)

One tool, in particular, seems very powerful: std::is_same<T1, T2> . Using this tool, one can achieve compile-time “magic” based on equality of two types.  For example.

if T is found at compile time to be of type char, branch into one algorithm, if not branch to another one. Very simple and powerful indeed. What could possibly go wrong?

And you are ready for a serious C++. But.  Consider this canonical function template:

Somehow,  T is almost never a type you expect it to be. And now you have to admit, probably you have not read the fundamentals of standard C++ before starting this journey on “the path peppered with the shards of glass”?  My  preferred answer to that simple question “what is the type of T” is:

One needs to know what exactly the type T can be

Side note: Speaking of types we immediately bump into SFINAE. SFINAE is one thing almost every C++ beginner puts under the carpet as long as possible. Until it has to be understood and used. This tool will help you too in that noble endeavor.

The core of success is your complete understanding of the classification of types in standard C++. Here is a quick set of C++ type names divided into two base categories:

  1. Compound types
    Array, function, object pointer, function pointer, member object pointer, member function pointer, reference, class, union, or enumeration, including any cv-qualified variants.
  2. Fundamental types
    Arithmetic type (integral type or a floating-point type), void, or nullptr_t. Where the integral types are:  bool, char, char16_t, char32_t, wchar_t, short, int, long, long long, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants

Hint: that “cv-qualified variants” is where the trouble starts.

Here is the situation recap. You have bravely dived into the standard C++ types taxonomy, and you have developed the habit to use standard and useful <type_traits> to fight it out with the types. And it seems so simple and logical when looking into examples online. But not always so, when you try it yourself.

The Solution

To help myself out ( and for painless use of other C++ std::  templated goodies), I have developed a few useful template aliases, for taming this uncertain situation around the “What is the type of  T” proverbial question.

Here I will present a tool that will reduce ‘anything’ to the base type.   Please look again, into that classification above. Seriously. Back? Ok. So,  here is a tool that will reduce any type T to its base fundamental type. Its most canonical usage:

Big note.

“Base type” is not a fundamental type.

The base type is a fundamental unqualified type which is a base of a compound type T. And if T is not compound type, in that case, this tool gives the unqualified form of T.

Much like std::decay<T> does. Actually this tools is something like std decay on steroids. To use the blogger’s cliche.

The Usage

To clarify this completely, let me show you some hard testing first. Here we use several funny but legal C++ compound types, invented for testing only.

All compile-time tets. As you can see  dbj::to_base_t<T>  really works. Whatever (compound) type you give to it, the result will be reduced to its unqualified base type.


This tool will also work for double, triple, etc pointers. And here is the tool itself:

Your problem function from above will now be changed to use dbj::to_base_t .

And remember the return type form that function can be only one. One C++ function can return only one type.

Feel free to copy-paste and use it. Just please leave in the copyright statement.

Obligatory Gist is here. That works for C++14, 17, and 20.