如何找到变量的类型?

6
我希望找出变量的类型(变量由模板参数给出,因此我不知道它是什么)。
#include <iostream>
#include <typeinfo>

int main() 
{
    double test;
    std::cout << typeid(test).name() << std::endl;
}

但是代码只输出: $./test

d

但我需要的是double类型。

关键在于,我不知道要期望哪种类型,但是我必须在子程序中写出它,并进行编译。所以使用d并不是个好主意。


1
如果它是一个模板参数,那么该参数本身不就是类型吗?! - Shahbaz
3
您想要的做法没有标准方式。名称表示是由实现定义的,如果我没记错,实现甚至可以不提供表示。这些都只适用于多态类型,对于非多态类型祝您好运。 - Khaled Alshaya
3
我猜你想做某件事,但尚未解释清楚。为什么你需要类型名称?我相信有一种方法可以解决你实际的问题,而不仅仅是你提出的那个问题。 - drdwilcox
1
你应该使用一个能够展示你问题的例子。在当前的例子中没有任何模板参数,因此你想要解决的主要问题并没有被这个例子所解释... - sth
1
@AraK:从我的C++0x标准草案中看5.2.8/3,似乎你也可以为非多态类型获取std::type_info对象。当然,并不是所有编译器都已经实现了标准的每一个细节,而且name()返回的是某些实现定义的内容。 - David Thornley
显示剩余2条评论
4个回答

3

如果你知道必须支持的类型列表,你可以编写自己的函数来实现:

template <typename T>
void printtype()
{
  if (typeid(T) == typeid(double))
    std::cout << "double";
  else if (typeid(T) == typeid(int))
    std::cout << "int";
}

请注意,由于该函数没有类型为T的参数,因此必须始终显式声明其类型:

printtype<double>()

当然,类型也可以作为参数类型:

printtype<U>()

工作正常,但我不知道类型。 我知道它们将由double、int和float组成,但可能是任何结构体。 - cl_progger

2
在GNU ABI中,有一个辅助工具可以解开typeidname()。请注意,GNU ABI只支持解开GNU ABI的名称(甚至可能不支持各种不同版本)。更多信息请参考这里
#include <cxxabi.h>
#include <stdlib.h>
#include <string>

template <typename T> std::string nameofType(const T& v)
{
    int     status;
    char   *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
    std::string name(realname? realname : "????");
    free(realname);

    return name;
}

现在,用漂亮的C++包装函数完成了。至少对我自己有个参考! - sehe
1
请在描述中注明它不具备可移植性。 - Kashyap
@thekashyap 嗯,就GNU ABI可移植的范围而言,它是可移植的(第二个和第三个单词,全大写;很难错过)。只是在其他无数编译器下无法工作。好的,那我会加强措辞的。 - sehe
这看起来像是一个未来类似问题中偶尔会被粘贴的答案 :-) (顺便问一下,你是不是指的 std::free?) - Kerrek SB
@KerrekSB:嘿呀,我曾经对此感到非常困惑(可能是旧版本的GNU glibc/libstdc++没有正确执行操作)。现在我可以验证,现在可以使用std::*(但是,包括<cstdlib>_确实_定义了::free而不需要提示)。无论如何,已经修复了另一种方式的示例,这里不需要混淆人们不需要的c++0x :) - sehe

2

您可以尝试在模板中使用该表达式作为参数来强制出现错误,编译器将返回您所需类型的错误信息。

例如,在GCC中:

#include <map>
#include <string>

template<typename T> void ErrorType(T &t)
{
    char x[sizeof(t)==0 ? 1 : -1];
}
template<typename T> void ErrorType(const T &t)
{
    char x[sizeof(t)==0 ? 1 : -1];
}

int main()
{
    double d = 3;
    const double cd = 3;
    ErrorType(d);
    ErrorType(cd);
    ErrorType(3);

    std::map<std::string, int> x;
    ErrorType(x.begin());
}

$ g++ -c test.cpp

test.cpp: In functionvoid ErrorType(T&) [with T = double]’:
test.cpp:17:20:   instantiated from here
test.cpp:6:14: error: size of array is negative
test.cpp: In functionvoid ErrorType(const T&) [with T = double]’:
test.cpp:18:21:   instantiated from here
test.cpp:10:14: error: size of array is negative
test.cpp: In functionvoid ErrorType(const T&) [with T = int]’:
test.cpp:19:20:   instantiated from here
test.cpp:10:14: error: size of array is negative
test.cpp: In functionvoid ErrorType(const T&) [with T = std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, int> >]’:
test.cpp:22:28:   instantiated from here
test.cpp:10:14: error: size of array is negative

因此,被转储的类型是doubleintstd::_Rb_tree_iterator<std::pair<const std::basic_string<char>, int> >。第一个重载是非const的,这意味着表达式是非const l-value。 sizeof(t)==0的技巧是为了使整个表达式依赖于模板参数,并延迟错误直到实例化。错误本身(数组大小为负)当然是无意义的。
如果您正在使用C++11,您可以进行改进:
#include <map>
#include <string>

template<typename T> void ErrorType(T &&t)
{
    static_assert(sizeof(t)==0, "Reporting type name");
}



int main()
{
    double d = 3;
    const double cd = 3;
    ErrorType(d);
    ErrorType(cd);
    ErrorType(3);

    std::map<std::string, int> x;
    ErrorType(x.begin());
}

$ g++ -c test.cpp  -std=gnu++0x


test.cpp: In functionvoid ErrorType(T&&) [with T = double&]’:
test.cpp:15:20:   instantiated from here
test.cpp:6:9: error: static assertion failed: "Reporting type name"
test.cpp: In functionvoid ErrorType(T&&) [with T = const double&]’:
test.cpp:16:21:   instantiated from here
test.cpp:6:9: error: static assertion failed: "Reporting type name"
test.cpp: In functionvoid ErrorType(T&&) [with T = int]’:
test.cpp:17:20:   instantiated from here
test.cpp:6:9: error: static assertion failed: "Reporting type name"
test.cpp: In functionvoid ErrorType(T&&) [with T = std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, int> >]’:
test.cpp:20:28:   instantiated from here
test.cpp:6:9: error: static assertion failed: "Reporting type name"

作为额外的奖励,这可以区分const l-values和r-values。第一个是一个double l-value,第二个是一个const double l-value,其他两个都是r-values。

1
注意:type_info 的成员名称返回的字符串取决于编译器和库的具体实现。它不一定是一个简单的字符串,带有其典型的类型名称,就像用于生成此输出的编译器中一样。
在此示例中,我们编译器在调用 type_info::name 时返回了易于人类理解的名称,但这并非要求:编译器可以返回任何字符串。
我的 GCC 生成了混淆的名称。例如,d 表示 double,i 表示 int,c 表示 char,St6vectorIiSaIiEE 表示 std::vector。
来自 GCC 的 typeinfo:
/** Returns an @e implementation-defined byte string; this is not
 *  portable between compilers!  */
const char* name() const
{ return __name[0] == '*' ? __name + 1 : __name; }

--编辑--

无法在编译器之间“可重复地”获取名称。如果要完全这样做,您必须像约翰·戈登在他的文章中描述的那样硬编码。


我需要在gcc中使用特殊的编译器标志吗?错误:‘__name’未在此范围内声明。 - cl_progger
1
在 GGG 中,您可以使用编译器扩展来解构名称 - Kerrek SB
1
@cl_progger 我引用了typeinfo头文件中的内容来强调“实现定义”和“不可移植”的短语。__name是typeinfo的实现,不要尝试使用它。 - Kashyap
@thekashyap:当然。我不认为你需要做任何这些来进行正确的编程;说实话,我不太确定OP想要什么。 - Kerrek SB
1
@KerrekSB 是的..他提到了关于将这些信息传递给一些OpenCL之类的东西,但我猜他得到了答案(“不可能具有可移植性”),所以他已经离开了。:) 我个人认为除了调试自己的模板代码外,没有其他用途。 - Kashyap
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接