web-dev-qa-db-fra.com

Comment pouvons-nous afficher le type valeur (value_type) d'un conteneur STL C++?

Je sais que les conteneurs STL ont un paramètre value_type et j'ai vu comment l'utiliser pour déclarer un type de variable comme:

vector<int>::value_type foo;

Mais pouvons-nous simplement imprimer ce value_type sur une console?

Je veux voir "chaîne" dans cet exemple:

int main() {
    vector<string> v = {"Apple", "facebook", "Microsoft", "google"};
    cout << v << endl;
    cout << v.value_type << endl;
    return 0;
}
14
Niakrais

X::value_type n'est pas différent de tout autre alias de type (aka typedef) à cet égard - C++ ne dispose d'aucun moyen natif de convertir un type en représentation de chaîne de son code source (notamment parce que cela pourrait être ambigu). Ce que vous pouvez faire, c'est utiliser std::type_info::name:

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

Le texte résultant dépend du compilateur (et n'est même pas garanti d'être facilement lisible par l'homme). Il sera cohérent dans la même génération d'un programme, mais vous ne pouvez pas vous attendre à ce qu'il soit identique parmi les différents compilateurs. En d'autres termes, il est utile pour le débogage, mais ne peut pas être utilisé de manière fiable de manière persistante.

15
Angew

En plus de la réponse basée sur typeid-, je vois une autre possibilité d'utiliser Boost comme ceci:

#include <boost/type_index.hpp>

cout << boost::typeindex::type_id_with_cvr<decltype(v)::value_type>().pretty_name() << "\n";

L'avantage est un nom portable, lisible par l'homme, qui inclut les qualifications const/volatile et qui est cohérent sur tous les principaux compilateurs et systèmes. La sortie sur ma machine est

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

Décidez vous-même si cette sortie est qualifiée de "lisible par l'homme", mais comparez-la également à l'approche typeid(...).name(), qui donne sur ma machine:

NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
5
lubgr

J'ai eu une question plutôt similaire il y a quelque temps. La réponse montrait une fonction capable de traduire un type en une chaîne lisible par l'homme:

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

Une fois que vous avez cette fonction TypeName, vous pouvez atteindre votre objectif:

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

Ce qui devrait sortir (GCC HEAD 9 201809):

std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
1
PaperBirdMaster

Il est probablement probable que safe suppose que vous avez besoin de ces informations de type à des fins de débogage (?) . Si c'est le cas, n'oubliez pas les commandes gdb construites dans whatis et 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> >>
1
OrenIshShalom

Si c'est à des fins de débogage, cette solution fonctionnerait sans aucune dépendance:

#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;
}

Contrairement à la réponse basée sur typeid, elle utilise le préprocesseur et n’est donc pas sujette à une modification de nom (le nom est donc ... lisible ).

Veuillez noter que vous ne pouvez pas utiliser le type v.value_type directement, il ne fait pas partie de l'instance v mais du type v: std::vector<std::string>.

La partie regex est due au fait que la classe std::string est trop stupide et que vous avez besoin de telles bizarreries pour une simple recherche/code divisé. Si vous avez une classe de chaîne décente, vous n’avez pas besoin de regex ici pour extraire la partie qui se trouve après la chaîne "=" et avant la ";".

1
xryl669