【问题标题】:How do we print out the value_type of a C++ STL container?我们如何打印出 C++ STL 容器的 value_type?
【发布时间】:2018-09-03 09:06:07
【问题描述】:

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

vector<int>::value_type foo;

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

我想在这个例子中看到“字符串”:

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

【问题讨论】:

  • 符号value_type 不是成员变量,它是一个作用域类型名。您希望能够打印关键字int 的值吗?
  • 它只是一个类型,所以你想得到类型名称作为字符串?
  • 很可能,但您需要添加一些代码。
  • 相当于cout &lt;&lt; string;,与cout &lt;&lt; "string";不同。
  • 是的@molbdnilo 我想要一个类型作为人类可读的字符串

标签: c++ stl


【解决方案1】:

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

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

生成的文本取决于编译器(甚至不能保证易于人类阅读)。它在程序的同一个版本中是一致的,但你不能期望它在不同的编译器中是相同的。换句话说,它对调试很有用,但不能以持久的方式可靠地使用。

【讨论】:

  • 谢谢,在我的机器上它给了我NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
  • @您可以使用__cxa_demangle 获取可读的类型名称:stackoverflow.com/a/23266701/1968
  • 不只是gcc吗?
  • @Niakrais:不完全是,它适用于所有使用 Itanium ABI 的编译器/平台组合。所有主流 C++ 编译器都在所有 Linux/MacOS 平台上使用安腾 ABI;唯一不使用它的广泛平台是 Windows,因此 MSVC 兼容模式下的 MSVC 和 Clang 将不会使用它。即使在 Windows 上,使用 Cygwin 或 MinGW 也意味着使用 Itanium ABI。
【解决方案2】:

除了基于 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

【讨论】:

    【解决方案3】:

    可能假设您出于调试目的需要此类型信息是安全的(?)。 如果是,别忘了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> >>
    

    【讨论】:

      【解决方案4】:

      前段时间我有一个相当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> >
      

      【讨论】:

        【解决方案5】:

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

        #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&lt;std::string&gt;

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

        【讨论】:

        • __FUNCTION__ 仅打印函数名称,没有任何附加信息。你想要__FUNCSIG__
        • 刚刚在这里尝试过:rextester.com/FDZT51977,它似乎适用于 FUNCTION。使用 FUNCSIG,由于签名更复杂,因此必须修改正则表达式。在所有情况下,谢谢,我以前不知道 FUNCSIG
        • 没关系,你是对的。 __FUNCTION__不包含函数本身的模板参数,所以之前我使用这个技巧时,我使用了__FUNCSIG__。但是它错过了我,它包含封闭类的模板参数。这是个好主意。
        猜你喜欢
        • 1970-01-01
        • 2016-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-02
        • 1970-01-01
        • 2012-07-21
        • 2021-12-06
        相关资源
        最近更新 更多