16

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

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

4 回答 4

18

链接时间解决方案:

在我的平台(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&
于 2013-08-22T00:19:17.323 回答
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&>

和铿锵++

错误:未定义模板的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>]

它很丑,但很管用。

于 2013-09-08T11:30:02.193 回答
2

让编译器向您显示变量的类型(可能以一种迂回的方式);

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

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

于 2013-08-21T23:27:28.750 回答
1

从Ali 的回答中触发编译器诊断的一种更简洁的方法是使用已删除的函数。

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&]

于 2018-10-09T02:34:36.797 回答