Как мы печатаем значение 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 здесь для извлечения части, которая после строки "=" и перед ";".