KnowItAllWannabe
KnowItAllWannabe

Reputation: 13512

How can I see the type deduced for a template type parameter?

Is there an easy way to force compilers to show me the type deduced for a template parameter? For example, given

template<typename T>
void f(T&& parameter);

const volatile int * const pInt = nullptr;
f(pInt);

I might want to see what type is deduced for T in the call to f. (I think it's const volatile int *&, but I'm not sure.) Or given

template<typename T>
void f(T parameter);

int numbers[] = { 5, 4, 3, 2, 1 };
f(numbers);

I might want to find out if my guess that T is deduced to be int* in the call to f is correct.

If there's a third-party library solution (e.g., from Boost), I'd be interested to know about it, but I'd also like to know if there's an easy way to force a compilation diagnostic that would include the deduced type.

Upvotes: 20

Views: 4416

Answers (6)

nmd_07
nmd_07

Reputation: 706

Check compiler's abstract syntax tree (AST). For example, the clang compiler's abstract syntax tree gives really detailed information about how templates are deduced.

Upvotes: 0

QingJia Wang
QingJia Wang

Reputation: 121

You can use godbolt to easily examine the generated demangled assembly code. Use your code as an example:

template<typename T>
void f(T&& parameter);

const volatile int * const pInt = nullptr;

int main()
{
    f(pInt);
}

The call xxx instruction will tell you the exact type deducted by the compiler.

main:
        push    rbp
        mov     rbp, rsp
        mov     edi, OFFSET FLAT:pInt
        call    void f<int const volatile* const&>(int const volatile* const&)
        mov     eax, 0
        pop     rbp
        ret

T is deduced to the type inside <>, which is int const volatile* const&.

call    void f<int const volatile* const&>(int const volatile* const&)

Upvotes: 0

Jake Cobb
Jake Cobb

Reputation: 1861

A slightly more succinct way to trigger the compiler diagnostic from Ali's answer is with a deleted function.

template <typename T>
void f(T&&) = delete;

int main() {
    const volatile int * const pInt = nullptr;
    f(pInt);
    return 0;
}

With GCC 8.1.0, you get:

error: use of deleted function 'void f(T&&) [with T = const volatile int* const&]

Upvotes: 3

Ali
Ali

Reputation: 58461

I have tried the followings with g++ 4.7.2 and clang++ 3.4 (trunk 184647); they both give

a compile-time error and the error message is containing the deduced type.

I have no access to MSVC 12, please check what happens and provide feedback.

#include <string>

template <typename T>
struct deduced_type;


template<typename T>
void f(T&& ) {

    deduced_type<T>::show;
}

int main() {

    f(std::string()); // rvalue string

    std::string lvalue;

    f(lvalue);

    const volatile int * const pInt = nullptr;

    f(pInt);
}

The error messages: g++ 4.7.2

error: incomplete type deduced_type<std::basic_string<char> > used in nested name specifier
error: incomplete type deduced_type<std::basic_string<char>&> used in nested name specifier
error: incomplete type deduced_type<const volatile int* const&> used in nested name specifier

and clang++

error: implicit instantiation of undefined template deduced_type<std::basic_string<char> >
error: implicit instantiation of undefined template deduced_type<std::basic_string<char> &>
error: implicit instantiation of undefined template deduced_type<const volatile int *const &>

The note / info messages also contain the type of f with both compilers, for example

In instantiation of void f(T&&) [with T = std::basic_string<char>]

It's butt-ugly but works.

Upvotes: 8

Howard Hinnant
Howard Hinnant

Reputation: 218770

Link time solution:

On my platform (OS X), I can get the linker to give me this information by simply making a short program that is complete, minus the definition of the function I'm curious about:

template<typename T>
void f(T&& parameter);  // purposefully not defined

int
main()
{
    const volatile int * const pInt = nullptr;
    f(pInt);
}

Undefined symbols for architecture x86_64:
  "void f<int const volatile* const&>(int const volatile* const&&&)", referenced from:
      _main in test-9ncEvm.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Admittedly I get the "triple reference", which should be interpreted as an lvalue reference (due to reference collapsing), and is a demangling bug (perhaps I can get that fixed).


Run time solution:

I keep a type_name<T>() function handy for this type of thing. A completely portable one is possible, but sub-optimal for me. Here it is:

#include <type_traits>
#include <typeinfo>
#include <string>

template <typename T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::string r = typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

I can use it like:

#include <iostream>

template<typename T>
void f(T&& parameter)
{
    std::cout << type_name<T>() << '\n';
}

int
main()
{
    const volatile int * const pInt = nullptr;
    f(pInt);
}

which for me prints out:

PVKi const&

That's not terribly friendly output. Your experience may be better. My platform ABI is based on the Itanium ABI. And this ABI includes this function:

namespace abi
{
    extern "C"
    char*
    __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
}

I can use this to demangle C++ symbols into a human readable form. An updated type_name<T>() to take advantage of this is:

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cstdlib>
#include <cxxabi.h>

template <typename T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
        (
            abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
            std::free
        );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

And now the previous main() prints out:

int const volatile* const&

Upvotes: 21

Mike Makuch
Mike Makuch

Reputation: 1838

To get the compiler to show you the type of a variable (perhaps in a round about way);

T parameter;
....
void f(int x);
...
f(parameter);

compiler should complain that "T" cannot be converted to int, assuming that it actually can't.

Upvotes: 2

Related Questions