Как мы печатаем значение value_type контейнера STL C++?
Я знаю, что в контейнерах 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;
}
Ответы
Ответ 1
X::value_type
ничем не отличается от любого другого псевдонима типа (aka typedef) в этом отношении - C++ не имеет собственного способа преобразования типа в его представление строки исходного кода (не в последнюю очередь потому, что это может быть неоднозначным). Вы можете использовать std::type_info::name
:
cout << typeid(decltype(v)::value_type).name() << endl;
Полученный текст зависит от компилятора (и даже не гарантируется, что он легко читается человеком). Он будет согласован в рамках одной и той же сборки программы, но вы не можете ожидать, что она будет одинаковой для разных компиляторов. Другими словами, он полезен для отладки, но не может надежно использоваться в постоянном режиме.
Ответ 2
В дополнение к typeid
-based, я вижу еще одну возможность использования Boost следующим образом:
#include <boost/type_index.hpp>
cout << boost::typeindex::type_id_with_cvr<decltype(v)::value_type>().pretty_name() << "\n";
Преимущество - это переносимое, удобочитаемое человеком имя, которое включает в себя константу/неустойчивую квалификацию и совместимо с большинством компиляторов и систем. Выход на моей машине
// 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, встроенный в команды whatis
и ptype
:
$ 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
Некоторое время назад у меня был довольно похожий вопрос. Ответ там показал функцию, способную перевести тип в удобочитаемую строку:
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<std::string>
Часть регулярного выражения состоит в том, что класс std::string
слишком тупой и вам нужны такие странности для простого кода поиска/разделения. Если у вас есть достойный класс строк, вам не понадобится regex здесь для извлечения части, которая после строки "=" и перед ";".