Reputation: 13512
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
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
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
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
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
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
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