【问题标题】:How can I see the type deduced for a template type parameter?如何查看为模板类型参数推导出的类型?
【发布时间】:2013-08-21 23:11:52
【问题描述】:

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

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

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

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

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

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

我可能想知道我在调用f 时推断Tint* 的猜测是否正确。

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

【问题讨论】:

  • 您希望在编译期间或运行时进行此操作?后者可以使用#include &lt;typeinfo&gt;typeid(T).name()
  • std::is_same&lt;T, const volatile int*&amp;&gt;::value?
  • @TemplateRex:我想在编译期间查看类型。
  • @Rapptz:一般来说,我想看看编译器推导出了什么类型,而不是猜测它推导了什么,然后看看我的猜测是否正确。
  • @KnowItAllWannabe 没有运行时类型信息是不可能的。

标签: c++ templates c++11


【解决方案1】:

链接时间解决方案:

在我的平台 (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&lt;T&gt;() 函数。一个完全便携的是可能的,但对我来说不是最佳的。这里是:

#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&lt;T&gt;() 可以利用这一点:

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

【讨论】:

  • 这很有用,谢谢。我还发现类似的信息可以通过 gcc 和 clang 下的 __PRETTY_FUNCTION__ 以及 MSVC 下的 __FUNCSIG__ 获得。这些产生字符串,因此它们导致运行时解决方案。
  • @KnowItAllWannabe 除非您在例如static_assert
  • @sehe:唉,__PRETTY_FUNCTION__ 就像一个变量,而不是字符串文字,所以它不能在 static_assert 中使用。
  • @HowardHinnant 我相信这可以在编译时解决。如果您对我的回答提供反馈,我将不胜感激。我不是专家,可能错过了一些重要的观点或一些极端情况。非常感谢!
  • @HowardHinnant 至少在 gcc 4.7.2 上,如果您将 nullptr 作为第四个参数传递给 abi::__cxa_demangle(),它不会再破坏名称。即使您稍后忽略错误代码,也请考虑将指针传递给本地 int 变量。在这种情况下,我非常感谢您对 my previous answer 的反馈。
【解决方案2】:

我已经使用 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&lt;std::basic_string&lt;char&gt; &gt; 用于嵌套名称说明符
错误:不完整的类型 deduced_type&lt;std::basic_string&lt;char&gt;&amp;&gt; 用于嵌套名称说明符
错误:不完整的类型 deduced_type&lt;const volatile int* const&amp;&gt; 用于嵌套名称说明符

和clang++

错误:未定义模板的隐式实例化deduced_type&lt;std::basic_string&lt;char&gt; &gt;
错误:未定义模板的隐式实例化 deduced_type&lt;std::basic_string&lt;char&gt; &amp;&gt;
错误:未定义模板的隐式实例化deduced_type&lt;const volatile int *const &amp;&gt;

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

void f(T&amp;&amp;) [with T = std::basic_string&lt;char&gt;]的实例化中

虽然很丑,但是很管用。

【讨论】:

  • @HowardHinnant 感谢您的检查。 (我在 3 小时前对您的回答投了赞成票。)
【解决方案3】:

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

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

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

【讨论】:

  • 这是一个有趣的想法,但是,至少对于 gcc 4.8.1 和 MSVC 12,它有缺点。在第一个示例中,gcc 报告类型两次,一次为const volatile int * const &amp;&amp;&amp;(是的,带有三个&符号!),一次为const volatile int * const。 MSVC 还会报告该类型两次,一次为volatile const int *const,一次为volatile const int *const &amp;
【解决方案4】:

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&amp;&amp;) [with T = const volatile int* const&amp;]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-07
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多