Ermitteln Sie zur Kompilierzeit, ob ein Typ ein STL-Container ist

Lesezeit: 13 Minuten

Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
Xander Tulpe

Ich möchte eine Vorlage schreiben, die bestimmt, ob ein Typ zur Kompilierzeit ein STL-Container ist.

Ich habe das folgende Stück Code:

struct is_cont{};
struct not_cont{};

template <typename T>
struct is_cont { typedef not_cont result_t; };

aber ich bin mir nicht sicher, wie ich die notwendigen Spezialisierungen für erstellen soll std::vector<T,Alloc>, deque<T,Alloc>, set<T,Alloc,Comp> etc…

  • Ich wäre neugierig zu wissen, warum es genau einer der STL-Container der Standardbibliothek sein muss. Wäre es nicht sinnvoll, die Eigenschaften des Containers zu überprüfen, damit etwas anderes, das diesen Eigenschaften entspricht, problemlos verwendet werden kann?

    – Nicol Bolas

    23. Februar 2012 um 5:01 Uhr

  • std::unordered_map war nicht in der STL, aber jetzt in C++11. Betrachten Sie es als STL-Typ?

    – MSalter

    23. Februar 2012 um 12:00 Uhr

  • @MSalters: Es tut Containeranforderungen erfüllen.

    – Billy ONeal

    9. Dezember 2012 um 23:00 Uhr

  • Wenn Sie entschieden haben, dass ein Container ein STL-Container ist, was werden Sie tun?

    – Raseh

    1. Mai 2013 um 13:45 Uhr

  • Bin neugierig, in welchem ​​​​Szenario / Anwendungsfall würde man eine solche Vorlage verwenden? Weil jeder generische Code, der einen Vektor verwenden würde, keinen assoziativen Container wie map verwenden kann. Wenn der Benutzer Ihrer Vorlage dies nur fragt, damit er dann iterieren kann, existieren die Nicht-Member-STL-Funktionen std::begin() und std::end() (cbegin/cend) hauptsächlich für diesen Zweck

    – Chenna V

    25. Juni 2017 um 18:30 Uhr

Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
Nawaz

Notiz: Der folgende Code stammt von einem ausgezeichneten Dienstprogramm namens hübscher Druck geschrieben von @Kerrek SB (ein Thema dazu bei stackoverflow).

Haftungsausschluss: Ich weiß nicht, ob ich diesen Code kopieren und hier einfügen darf, ohne die Erlaubnis des ursprünglichen Autors einzuholen. @Kerrek, lass es mich wissen, wenn du ein Problem hast. 🙂


Sie können diese Klassenvorlage verwenden:

  template<typename T> 
  struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> 
  { };

Verwendungszweck:

 std::cout << is_container<std::vector<int>>::value << std::endl; //true
 std::cout << is_container<std::list<int>>::value << std::endl;   //true 
 std::cout << is_container<std::map<int>>::value << std::endl;    //true
 std::cout << is_container<std::set<int>>::value << std::endl;    //true
 std::cout << is_container<int>::value << std::endl;              //false

Beachten Sie, dass is_container benötigt folgende Hilfsklassenvorlagen:

template<typename T>
struct has_const_iterator
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::const_iterator*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
    typedef T type;
};

template <typename T>
struct has_begin_end
{
    template<typename C> static char (&f(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&f(...))[2];

    template<typename C> static char (&g(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&g(...))[2];

    static bool const beg_value = sizeof(f<T>(0)) == 1;
    static bool const end_value = sizeof(g<T>(0)) == 1;
};

  • Nette Sache, aber es erkennt nicht-std-Typen, die zufällig std-Konventionen folgen (dh Iteratoren und Anfangs-/Endpaare haben). Das ist gar nicht so selten!

    – Bitmaske

    23. Februar 2012 um 5:16 Uhr

  • @bitmask: Ja, weil es völlig generisch ist. 🙂

    – Nawaz

    23. Februar 2012 um 5:17 Uhr


  • @Nawaz: Das ist ein netter Ansatz, aber es ist ein bisschen mehr als das, was erforderlich war.

    – Xander-Tulpe

    23. Februar 2012 um 5:20 Uhr

  • Da gibt es keinen Streit. Aber Generizität macht für den Schöndrucker Sinn, eignet sich aber nur als reine Heuristik für diese Frage.

    – Bitmaske

    23. Februar 2012 um 5:22 Uhr

  • @ViktorSehr: Ich vertraue VS2012 nicht.

    – Nawaz

    11. März 2014 um 6:49 Uhr

1646263209 611 Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
Vaughn Cato

Zuerst definieren Sie Ihre primäre Vorlage, die ein Mitglied hat, das im Standardfall falsch ist:

template <typename T>
struct is_cont {
  static const bool value = false;
};

Dann definieren Sie partielle Spezialisierungen für Ihre Containertypen, die stattdessen den Wert true haben:

template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
  static const bool value = true;
};

Verwenden Sie dann für einen Typ X, den Sie überprüfen möchten, ihn wie

if (is_cont<X>::value) { ... } 

  • Ich hatte den gleichen Ansatz, das Problem ist, dass STL-Container mehr als 1 Template-Parameter haben, zB: Alloc, Comp etc …

    – Xander-Tulpe

    23. Februar 2012 um 5:06 Uhr

  • Beachten Sie, dass dies fehlschlägt, wenn Sie nicht standardmäßige Parameter für angeben vector. Sie sollten alle Vorlagenparameter weiterleiten, um generisch zu sein.

    – Bitmaske

    23. Februar 2012 um 5:08 Uhr

  • Richtig – ich habe meine Antwort allgemeiner gemacht.

    – Vaughn Cato

    23. Februar 2012 um 5:09 Uhr

Verfolgen Sie den Vorschlag, dass ein generischer Kompilierzeittest für
hat-eine-stl-containerähnliche-schnittstelle wäre eine sinnvolle lösung, diese definiert man an stl-ähnlicher Container T

über die Schnittstelle:

T::iterator T::begin();
T::iterator T::end();
T::const_iterator T::begin() const;
T::const_iterator T::end() const;

*T::iterator is T::value_type &
*T::const_iterator is T::value_type const &

Zusätzliche Anforderungen, zB a size() -Methode auf offensichtliche Weise hinzugefügt werden, oder andere kanonische Typschnittstellen, die zur Kompilierzeit auf offensichtlich ähnliche Weise untersucht werden könnten.

#ifndef IS_STL_CONTAINER_LIKE_H
#define IS_STL_CONTAINER_LIKE_H

#include <type_traits>

template<typename T>
struct is_stl_container_like
{
    typedef typename std::remove_const<T>::type test_type;

    template<typename A>
    static constexpr bool test(
        A * pt,
        A const * cpt = nullptr,
        decltype(pt->begin()) * = nullptr,
        decltype(pt->end()) * = nullptr,
        decltype(cpt->begin()) * = nullptr,
        decltype(cpt->end()) * = nullptr,
        typename A::iterator * pi = nullptr,
        typename A::const_iterator * pci = nullptr,
        typename A::value_type * pv = nullptr) {

        typedef typename A::iterator iterator;
        typedef typename A::const_iterator const_iterator;
        typedef typename A::value_type value_type;
        return  std::is_same<decltype(pt->begin()),iterator>::value &&
                std::is_same<decltype(pt->end()),iterator>::value &&
                std::is_same<decltype(cpt->begin()),const_iterator>::value &&
                std::is_same<decltype(cpt->end()),const_iterator>::value &&
                std::is_same<decltype(**pi),value_type &>::value &&
                std::is_same<decltype(**pci),value_type const &>::value;

    }

    template<typename A>
    static constexpr bool test(...) {
        return false;
    }

    static const bool value = test<test_type>(nullptr);

};

#endif

Hier ist ein Testprogramm, erstellt mit GCC 4.7.2, Clang 3.2, Intel C++ 13.1.1:

#include "is_stl_container_like.h"

// Testing ...

#include <iostream>
#include <vector>
#include <array>
#include <functional>

using namespace std;

template<class C>
struct polymorphic : private C
{
    typedef typename C::value_type value_type;
    typedef typename C::iterator iterator;
    typedef typename C::const_iterator const_iterator;

    virtual ~polymorphic(){}

    virtual const_iterator begin() const {
        return C::begin();
    }

    virtual iterator begin()  {
        return C::begin();
    }

    virtual const_iterator end() const {
        return C::end();
    }

    virtual iterator end()  {
        return C::end();
    }   
};

template<class C>
struct reject : private C
{
    typedef typename C::value_type value_type;
    typedef typename C::iterator iterator;
    typedef typename C::const_iterator const_iterator;


    const_iterator begin() {
        return C::begin();
    }

    iterator begin() const {
        return C::begin();
    }

    const_iterator end() {
        return C::end();
    }

    iterator end() const {
        return C::end();
    }
};

int main()
{
    cout << is_stl_container_like<vector<int> const >::value << endl; // Yes
    cout << is_stl_container_like<array<int,42>>::value << endl; // Yes
    cout << is_stl_container_like<polymorphic<vector<int>>>::value << endl; // Yes
    cout << is_stl_container_like<function<int(int)>>::value << endl; // No
    cout << is_stl_container_like<int>::value << endl; // No
    cout << is_stl_container_like<reject<vector<int>>>::value << endl; //No
}

  • Vielleicht etwas spät, aber was macht argument *pv? Weil ich nicht sehe, dass es in der return-Anweisung verwendet wird und der Compiler (gcc 7.5.0) sich darüber beschwert, dass es nicht verwendet wird.

    – Michiel uit het Broek

    8. Juni 2020 um 12:50 Uhr


1646263210 208 Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
Trevor Hickey

Viele der bereits vorgeschlagenen Lösungen sind ausführlich zum Erkennen von STL-Containern.
Sie konzentrieren sich auf die Eigenschaften, die alle Container besitzen, anstatt explizit anzugeben, was die Container sind.

Wenn Sie eigene Container erstellen und mit einem echten Typ auswerten lassen möchten, empfehle ich die anderen Lösungen. Wenn Sie nur legitime STL-Container und keine STL-ähnlichen Container validieren möchten, ziehen Sie die Verwendung der folgenden Implementierung in Betracht, da sie eine präzise Erkennung von STL-Containern bietet:

#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <type_traits>

//specialize a type for all of the STL containers.
namespace is_stl_container_impl{
  template <typename T>       struct is_stl_container:std::false_type{};
  template <typename T, std::size_t N> struct is_stl_container<std::array    <T,N>>    :std::true_type{};
  template <typename... Args> struct is_stl_container<std::vector            <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::deque             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::list              <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::forward_list      <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::set               <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::multiset          <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::map               <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::multimap          <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_set     <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_multiset<Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_map     <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_multimap<Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::stack             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::queue             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::priority_queue    <Args...>>:std::true_type{};
}

//type trait to utilize the implementation type traits as well as decay the type
template <typename T> struct is_stl_container {
  static constexpr bool const value = is_stl_container_impl::is_stl_container<std::decay_t<T>>::value;
};

Beachten Sie die Verwendung von std::decay um eine falsche Typableitung basierend auf Typqualifizierern zu vermeiden. Außerdem haben wir Vererbung verwendet std::true_type und std::false_type um die Angabe zu vermeiden ::type tippt uns. Variadische C++11-Vorlagen werden verwendet, um die n Menge an Vorlagentypparametern abzuleiten, die zum Erstellen der Container erforderlich sind.

Die Verwendung der Implementierung ist wie erwartet:

  std::cout << std::boolalpha;
  std::cout << is_stl_container<std::vector<int>>::value << '\n';
  std::cout << is_stl_container<std::vector<int>const&>::value << '\n';
  std::cout << is_stl_container<int>::value << '\n';

Drucke:

true
true
false

In C++20 könnten Sie verwenden concept,

Es liegt an Ihnen, weitere Überprüfungen von dem hinzuzufügen, was Sie als Container betrachten, aber es könnte so aussehen:

template <typename T>
concept Container = requires(T t)
{
    std::begin
    std::end
};

Anwendungsbeispiel.

Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
kirill_igum

Es gibt is_container im Schub
http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html

is_container<C>::type — Ergebnis der Metafunktion, die zu mpl::true_ ausgewertet wird, wenn ein gegebener Typ C als Container behandelt werden soll, ansonsten mpl::false_ Im Allgemeinen muss sich jede Implementierung von is_container so verhalten, als wäre sie ein MPL-Boolean Konstante..

1646263212 207 Ermitteln Sie zur Kompilierzeit ob ein Typ ein STL Container ist
Marc Dirven

Getestet mit MSVC 2019:

template<class C>
struct IsContainer {
private:
    template<class D>
    static constexpr auto hasValueType() -> decltype(typename D::value_type(), std::true_type()) {
        return {};
    }

    template<class D>
    static constexpr auto hasIteratorAlias() -> decltype(typename D::iterator(), std::true_type()) {
        return {};
    }

    template<class D>
    static constexpr std::false_type hasIteratorAlias(...) {
        return {};
    }

    template<class D>
    static constexpr auto hasConstIteratorAlias() -> decltype(typename D::const_iterator(), std::true_type()) {
        return {};
    }

    template<class D>
    static constexpr auto hasBegin() -> decltype(decltype(std::begin(std::declval<D>())){}, std::true_type()) {
        return {};
    }

    template<class D>
    static constexpr std::false_type hasBegin(...) {
        return {};
    }

    template<class D>
    static constexpr auto hasEnd() -> decltype(decltype(std::end(std::declval<D>())){}, std::true_type()) {
        return {};
    }

    template<class D>
    static constexpr std::false_type hasEnd(...) {
        return {};
    }

    template<class D>
    static constexpr std::false_type hasConstIteratorAlias(...) {
        return {};
    }

    template<class D>
    static constexpr std::false_type hasValueType(...) {
        return {};
    }

public:
    constexpr static bool value = hasValueType<C>().value && 
        hasIteratorAlias<C>().value && 
        hasConstIteratorAlias<C>().value && 
        hasBegin<C>().value && 
        hasEnd<C>().value;
    
    constexpr bool operator()() const {
        return value;
    }
};

Verwendungszweck:

std::vector<int> vec;
int x = 0;
float y = 0.f;
std::array<int, 1> arr{};
    
constexpr auto val = IsContainer<decltype(vec)>()();
constexpr auto val2 = IsContainer<decltype(x)>()();
constexpr auto val3 = IsContainer<decltype(y)>()();
constexpr auto val4 = IsContainer<decltype(arr)>()();
    
std::cout << static_cast<bool>(val) << '\n';
std::cout << static_cast<bool>(val2) << '\n';
std::cout << static_cast<bool>(val3) << '\n';
std::cout << static_cast<bool>(val4) << '\n';

Ausgabe:

1
0
0
1

917180cookie-checkErmitteln Sie zur Kompilierzeit, ob ein Typ ein STL-Container ist

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy