如何查看模板类型参数的推导类型?

时间:2013-08-21 23:11:53

标签: c++ templates c++11

是否有一种简单的方法可以强制编译器向我显示模板参数的推导类型?例如,给定

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

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

我可能想要查看T调用中f推断出的类型。 (我认为这是const volatile int *&,但我不确定。)或者给予

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

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

我可能想知道,在T的调用中我推测int*推断为f是否正确。

如果有第三方库解决方案(例如,来自Boost),我有兴趣了解它,但我也想知道是否有一种简单的方法可以强制进行编译诊断,包括推论类型。

4 个答案:

答案 0 :(得分:11)

链接时间解决方案:

在我的平台(OS X)上,我可以让链接器通过简单地创建一个完整的简短程序来提供这些信息,减去我很好奇的函数的定义:

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)

不可否认,我得到了“三重参考”,应该被解释为左值参考(由于参考折叠),并且是一个解码错误(也许我可以解决这个问题)。


运行时解决方案:

我为这类事物保留了type_name<T>()函数。一个完全可移植的可能,但对我来说不是最佳的。这是:

#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;
}

我可以像:

一样使用它
#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);
}

对我来说打印出来:

PVKi const&

这不是非常友好的输出。你的经历可能会更好。我的平台ABI基于Itanium ABI。这个ABI包括这个功能:

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

我可以用它来将C ++符号解编成人类可读的形式。更新type_name<T>()以利用此功能:

#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;
}

现在先前的main()打印出来了:

int const volatile* const&

答案 1 :(得分:7)

我尝试了以下g ++ 4.7.2和clang ++ 3.4(trunk 184647);他们都给了

编译时错误,错误消息包含推断类型

我无法访问MSVC 12,请检查发生的情况并提供反馈。

#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);
}

错误消息:g ++ 4.7.2

错误:嵌套名称说明符中使用的不完整类型deduced_type<std::basic_string<char> > 错误:嵌套名称说明符中使用的不完整类型deduced_type<std::basic_string<char>&> 错误:嵌套名称说明符

中使用的类型deduced_type<const volatile int* const&>不完整

和clang ++

错误:隐式实例化未定义模板deduced_type<std::basic_string<char> >
错误:未定义模板deduced_type<std::basic_string<char> &>的隐式实例化 错误:未定义模板deduced_type<const volatile int *const &>

的隐式实例化

注释/信息消息还包含两个编译器的f类型,例如

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

的实例化中

这很丑陋但有效。

答案 2 :(得分:2)

让编译器向您展示变量的类型(可能是围绕着一种方式);

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

编译器应该抱怨“T”不能转换为int,假设它实际上不能。

答案 3 :(得分:0)

Ali's answer触发编译器诊断的一种更为简洁的方法是删除函数。

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

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

使用GCC 8.1.0,您将获得:

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