我们如何打印出C ++ STL容器的value_type?

问题描述 投票:14回答:5

我知道STL容器有一个value_type参数,我已经看到它如何用于声明一个类型的变量,如:

vector<int>::value_type foo;

但是我们可以将这个value_type打印到控制台吗?

我想在这个例子中看到“string”:

int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google"};
    cout << v << endl;
    cout << v.value_type << endl;
    return 0;
}
c++ stl
5个回答
15
投票

在这方面,X::value_type与任何其他类型的别名(aka typedef)没什么区别--C ++没有将类型转换为其源代码字符串表示的本地方式(至少因为它可能是模棱两可的)。你可以做的是使用std::type_info::name

cout << typeid(decltype(v)::value_type).name() << endl;

生成的文本依赖于编译器(甚至不能保证人类可读)。它将在程序的同一构建中保持一致,但您不能指望它在不同的编译器中是相同的。换句话说,它对调试很有用,但不能以持久的方式可靠地使用。


5
投票

除了基于typeid的答案之外,我还看到了使用Boost的另一种可能性:

#include <boost/type_index.hpp>

cout << boost::typeindex::type_id_with_cvr<decltype(v)::value_type>().pretty_name() << "\n";

优点是一个可移植的,人类可读的名称,包括const / volatile资格,并且在主要编译器和系统中是一致的。我机器上的输出是

// when compiled with gcc:
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
// ... and with clang:
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >

自己决定这个输出是否符合“人类可读”的标准,还要将它与typeid(...).name()方法进行比较,这种方法在我的机器上给出:

NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE

1
投票

假设您需要此类型信息用于调试目的(?),这可能是安全的。如果是,请不要忘记gdb内置的whatisptype命令:

$ cat main.cpp
#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google" };
    cout << v[2] << endl;
    // cout << v.value_type << endl;
    return 0;
}
$ cat gdbCommands.txt
break main
run
next
next
whatis v
quit
$ g++ -ggdb -o main main.cpp
$ gdb -x gdbCommands.txt ./main
GNU gdb (Ubuntu 8.0.1-0ubuntu1) 8.0.1
// bla bla bla ...
type = std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >>

1
投票

前段时间我有一个相当similar question。答案显示了一个能够将类型转换为人类可读字符串的函数:

template <typename T> std::string TypeName() {
    auto name = typeid(T()).name();  // function type, not a constructor call!
    int status = 0;

    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    std::string ret((status == 0) ? res.get() : name);
    if (ret.substr(ret.size() - 3) == " ()") ret.resize(ret.size() - 3);
    return ret;
}

一旦你有这个TypeName功能,你就可以实现你的目标:

int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google" };
    cout << v << endl;
    cout << TypeName<v.value_type>() << endl;
    return 0;
}

哪个应输出(GCC HEAD 9 201809):

std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >

1
投票

如果它是出于调试目的,那么这个解决方案可以在没有任何依赖性的情况下工作:

#include <regex>

template <typename T>
struct TypeToNameT
{
    static std::string getTypeFromName() {
#ifdef _MSC_VER
        std::regex re("<(.*)>::.*");
        std::string templateType(__FUNCTION__);
#else
        std::regex re(" = (.*);");
        std::string templateType(__PRETTY_FUNCTION__);
#endif
        std::smatch match;
        try
        {
            if (std::regex_search(templateType, match, re) && match.size() > 1)
                return match.str(1);
        }
        catch (std::regex_error& e) {
            // Syntax error in the regular expression
        }
        return "";
    }
};
/** Get the templated type name. This does not depends on RTTI, but on the preprocessor, so it should be quite safe to use even on old compilers */
template <typename T>
std::string getTypeName() { return TypeToNameT<T>::getTypeFromName(); }


int main() {
    std::vector<std::string> v = {"apple", "facebook", "microsoft", "google" };
    std::cout << getTypeName<decltype(v)::value_type>() << std::endl;  // Returns: "std::__cxx11::basic_string<char>"
    return 0;
}

与基于typeid的答案不同,这使用预处理器,因此不受名称修改(因此名称是......可读的)。

请注意,您不能直接使用v.value_type作为类型,它不是v实例的一部分,而是v的类型:std::vector<std::string>

正则表达式的部分是因为std::string类太笨了,你需要这么简单的搜索/分割代码。如果你有一个不错的字符串类,你不需要在这里提取正则表达式来提取“=”字符串之后和“;”之前的部分。

© www.soinside.com 2019 - 2024. All rights reserved.