Как распечатать value_type контейнера STL C++?

Я знаю, что контейнеры 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?

Some programmer dude 03.09.2018 11:08

это просто тип, поэтому вы хотите получить имя типа в виде строки?

apple apple 03.09.2018 11:08

Вполне возможно, но вам нужно добавить код.

bipll 03.09.2018 11:09

Это было бы эквивалентно cout << string;, а это не то же самое, что cout << "string";.

molbdnilo 03.09.2018 11:14

да @molbdnilo я хочу тип в виде читаемой человеком строки

Niakrais 03.09.2018 11:26

Возможный дубликат Шаблон, который печатает имя данного типа

Arne Vogel 03.09.2018 15:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
14
6
2 343
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

X::value_type ничем не отличается от любого другого псевдонима типа (также известного как typedef) в этом отношении - C++ не имеет собственного способа преобразования типа в его строковое представление исходного кода (не в последнюю очередь потому, что это может быть неоднозначным). Что вы можете сделать, так это использовать std::type_info::name:

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

Результирующий текст зависит от компилятора (и даже не гарантируется, что он будет легко читаемым человеком). Он будет согласован в одной и той же сборке программы, но нельзя ожидать, что он будет одинаковым для разных компиляторов. Другими словами, он полезен для отладки, но не может надежно использоваться постоянно.

Спасибо, на моей машине дает NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEE‌​EE

Niakrais 03.09.2018 11:24

@ Вы можете использовать __cxa_demangle, чтобы получить читаемое имя типа: stackoverflow.com/a/23266701/1968

Konrad Rudolph 03.09.2018 11:26

Разве это не только для gcc?

Niakrais 03.09.2018 11:30

@Niakrais: Не совсем, это для всех комбинаций компиляторов / платформ, использующих Itanium ABI. Itanium ABI используется на всех платформах Linux / MacOS всеми распространенными компиляторами C++; единственная широко распространенная платформа, не использующая его, - это Windows, и поэтому MSVC и Clang в режиме совместимости с MSVC не будут использовать его. Даже в Windows использование Cygwin или MinGW означает использование Itanium ABI.

Matthieu M. 03.09.2018 13:14

В дополнение к ответу на основе 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

Наверное, это можно с уверенностью предположить, что вам нужна эта информация о типе для целей отладки (?). Если это так, не забудьте встроенные в 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> >>

Некоторое время назад у меня был довольно аналогичный вопрос. Ответ там показал функцию, способную переводить тип в удобочитаемую строку:

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> >

Если это для целей отладки, то это решение будет работать без каких-либо зависимостей:

#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 слишком тупой, и вам нужны такие странности для простого кода поиска / разделения. Если у вас есть достойный строковый класс, вам не понадобится здесь регулярное выражение для извлечения части, которая находится после строки "=" и перед ";".

__FUNCTION__ печатает только имя функции без какой-либо дополнительной информации. Вам нужен __FUNCSIG__.
HolyBlackCat 03.09.2018 18:25

Просто попробовал вот здесь: rextester.com/FDZT51977 и вроде работает с НАЗНАЧЕНИЕ. При использовании FUNCSIG регулярное выражение должно быть изменено, поскольку подпись более сложна. Во всех случаях спасибо, я раньше не знал о FUNCSIG.

xryl669 03.09.2018 18:30

Да ладно, ты прав. __FUNCTION__ не содержит шаблонных параметров самой функции, поэтому, когда я использовал этот трюк раньше, я прибегал к использованию __FUNCSIG__. Но мне не хватало того, что он содержит параметры шаблона включающих классов. Хорошая идея.

HolyBlackCat 03.09.2018 18:33

Другие вопросы по теме