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

jjjwad0x  于 2023-05-02  发布在  其他
关注(0)|答案(5)|浏览(115)

有没有一种简单的方法来强制编译器显示为模板参数推导出的类型?例如,给定

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

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

我可能想看看在对f的调用中为T推导出了什么类型。(我想是const volatile int *&,但我不确定。)或给予

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

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

我可能想知道在调用f时,我关于T被推断为int*的猜测是否正确。
如果有第三方库解决方案(e.例如,来自Boost),我很有兴趣了解它,但我也想知道是否有一种简单的方法来强制编译诊断,其中包括推导出的类型。

v9tzhpje

v9tzhpje1#

链接时间解决方案:

在我的平台(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)

诚然,我得到了“三重引用”,它应该被解释为左值引用(由于引用折叠),这是一个demangling bug(也许我可以修复它)。

运行时解决方案:

我为这类事情准备了一个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&
lskq00tm

lskq00tm2#

我用g++ 4试了一下。7.2和clang++ 3。4( Backbone.js 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 &>的隐式示例化
note / info消息还包含两个编译器的f类型,例如
void f(T&&) [with T = std::basic_string<char>]的示例化中
很丑但很管用

ilmyapht

ilmyapht3#

让编译器向你显示变量的类型(也许是以一种迂回的方式);

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

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

2j4z5cfb

2j4z5cfb4#

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

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

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

GCC 8.10,你得到:
error: use of deleted function 'void f(T&&) [with T = const volatile int* const&]

2nbm6dog

2nbm6dog5#

您可以使用godbolt来轻松地检查生成的非标准汇编代码。使用您的代码作为示例:

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

const volatile int * const pInt = nullptr;

int main()
{
    f(pInt);
}

call xxx指令将告诉您编译器推导出的确切类型。

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被推导为<>内部的类型,即int const volatile* const&

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

相关问题