Как мы распечатываем value_type контейнера C ++ STL? - PullRequest
0 голосов
/ 03 сентября 2018

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

Ответы [ 5 ]

0 голосов
/ 03 сентября 2018

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

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

0 голосов
/ 03 сентября 2018

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

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> >
0 голосов
/ 03 сентября 2018

В дополнение к ответу на основе typeid я вижу еще одну возможность использования 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
0 голосов
/ 03 сентября 2018

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...