Comment écrire un trait de type 'est conteneur` ou`est vecteur'?

est - il possible d'écrire un trait de type dont la valeur est vraie pour toutes les structures STL communes (par exemple, vector,set, map,...)?

pour commencer, j'aimerais écrire un trait de type qui est vrai pour un vector et faux autrement. J'ai essayé ceci, mais cela ne compile pas:

template<class T, typename Enable = void>
struct is_vector {
  static bool const value = false;
};

template<class T, class U>
struct is_vector<T, typename boost::enable_if<boost::is_same<T, std::vector<U> > >::type> {
  static bool const value = true;
};

Le message d'erreur est template parameters not used in partial specialization: U.

22
demandé sur Frank 2012-08-20 22:11:40

10 réponses

Regard, une autre SFINAE à base de solution pour la détection de la STL comme des conteneurs:

template<typename T, typename _ = void>
struct is_container : std::false_type {};

template<typename... Ts>
struct is_container_helper {};

template<typename T>
struct is_container<
        T,
        std::conditional_t<
            false,
            is_container_helper<
                typename T::value_type,
                typename T::size_type,
                typename T::allocator_type,
                typename T::iterator,
                typename T::const_iterator,
                decltype(std::declval<T>().size()),
                decltype(std::declval<T>().begin()),
                decltype(std::declval<T>().end()),
                decltype(std::declval<T>().cbegin()),
                decltype(std::declval<T>().cend())
                >,
            void
            >
        > : public std::true_type {};

bien sûr, vous pouvez changer les méthodes et les types à vérifier.

si vous voulez détecter uniquement les conteneurs STL (cela signifie std::vector,std::list, etc), vous devriez faire quelque chose comme .

20
répondu Nevermore 2017-05-23 12:25:42

Vous dirais qu'il devrait être plus simple...

template <typename T, typename _ = void>
struct is_vector { 
    static const bool value = false;
};
template <typename T>
struct is_vector< T,
                  typename enable_if<
                      is_same<T,
                              std::vector< typename T::value_type,
                                           typename T::allocator_type >
                             >::value
                  >::type
                >
{
    static const bool value = true;
};

... Mais je ne suis pas vraiment sûr que ce soit plus simple ou pas.

En C++11, vous pouvez utiliser le type d'alias (je pense, non testé):

template <typename T>
using is_vector = is_same<T, std::vector< typename T::value_type,
                                          typename T::allocator_type > >;

Le problème avec votre approche est que le type U n'est pas déductible dans le contexte où il est utilisé.

16
répondu David Rodríguez - dribeas 2012-08-20 18:26:54

en Fait, après quelques tâtonnements, j'ai trouvé que c'est assez simple:

template<class T>
struct is_vector<std::vector<T> > {
  static bool const value = true;
};

je voudrais encore savoir comment écrire un plus général is_container. Dois-je énumérer tous les types à la main?

11
répondu Frank 2012-08-20 22:10:19

alors que les autres réponses ici qui tentent de deviner si une classe est un conteneur ou non pourrait fonctionner pour vous, je voudrais vous présenter l'alternative de nommer le type pour lequel vous voulez retourner true. Vous pouvez l'utiliser pour construire arbitraire is_(something) types de traits.

template<class T> struct is_container : public std::false_type {};

template<class T, class Alloc> 
struct is_container<std::vector<T, Alloc>> : public std::true_type {};

template<class K, class T, class Comp, class Alloc> 
struct is_container<std::map<K, T, Comp, Alloc>> : public std::true_type {};

Et ainsi de suite.

vous devrez inclure <type_traits> et toutes les classes que vous ajoutez à vos règles.

6
répondu Fozi 2012-08-20 19:06:37

pourquoi ne pas faire quelque chose comme ça pour is_container?

template <typename Container>
struct is_container : std::false_type { };

template <typename... Ts> struct is_container<std::list<Ts...> > : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...> > : std::true_type { };
// ...

ainsi les utilisateurs peuvent ajouter leurs propres conteneurs en se spécialisant partiellement. Comme pour is_vector et-al, utilisez la spécialisation partielle comme je l'ai fait ci-dessus, mais limitez-la à un seul type de conteneur, pas beaucoup.

6
répondu bstamour 2012-08-20 22:00:37
template <typename T>
struct is_container {

    template <
       typename U,
       typename I = typename U::const_iterator
    >   
    static int8_t      test(U* u); 

    template <typename U>
    static int16_t     test(...);

    enum { value  =  sizeof test <typename std::remove_cv<T>::type> (0) == 1 };
};


template<typename T, size_t N>  
struct  is_container <std::array<T,N>>    : std::true_type { };
1
répondu Leonid Volnitsky 2012-08-20 19:00:40

la façon dont j'aime détecter si quelque chose est un conteneur est de chercher data() et size() fonctions des membres. Comme ceci:

template <typename T, typename = void>
struct is_container : std::false_type {};

template <typename T>
struct is_container<T
   , std::void_t<decltype(std::declval<T>().data())
      , decltype(std::declval<T>().size())>> : std::true_type {};
1
répondu Arvid 2017-12-20 20:41:41

dans notre projet, nous n'avons pas encore réussi à migrer vers un compilateur supportant C++11, donc pour type_traits d'objets container j'ai dû écrire un simple helper de style boost:

template<typename Cont> struct is_std_container: boost::false_type {};
template<typename T, typename A> 
struct is_std_container<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_container<std::list<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_container<std::deque<T,A> >: boost::true_type {};
template<typename K, typename C, typename A> 
struct is_std_container<std::set<K,C,A> >: boost::true_type {};
template<typename K, typename T, typename C, typename A> 
struct is_std_container<std::map<K,T,C,A> >: boost::true_type {};

template<typename Cont> struct is_std_sequence: boost::false_type {};
template<typename T, typename A> 
struct is_std_sequence<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_sequence<std::list<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_sequence<std::deque<T,A> >: boost::true_type {};
0
répondu czarles 2017-06-30 14:13:33

si vous voulez aussi le faire fonctionner pour const std:: vector, vous pouvez utiliser ce qui suit:

namespace local {

template<typename T, typename _ = void>
struct isVector: std::false_type {
};

template<typename T>
struct isVector<T,
        typename std::enable_if<
                std::is_same<typename std::decay<T>::type, std::vector<typename std::decay<T>::type::value_type, typename std::decay<T>::type::allocator_type> >::value>::type> : std::true_type {
};

}

TEST(TypeTraitTest, testIsVector) {
    ASSERT_TRUE(local::isVector<std::vector<int>>::value);
    ASSERT_TRUE(local::isVector<const std::vector<int>>::value);

    ASSERT_FALSE(local::isVector<std::list<int>>::value);
    ASSERT_FALSE(local::isVector<int>::value);

    std::vector<uint8_t> output;
    std::vector<uint8_t> &output2 = output;
    EXPECT_TRUE(core::isVector<decltype(output)>::value);
    EXPECT_TRUE(core::isVector<decltype(output2)>::value);
}

sans le std:: remove_cv appeler la seconde ASSERT_TRUE serait un échec. Mais bien sûr, cela dépend de vos besoins. La chose ici est que, selon les spécifications, std::is_same vérifie const et volatile pour correspondre.

0
répondu Martin Gerhardy 2017-08-15 17:36:50

avant 2018 et C++17, j'osais tellement améliorer la réponse de @Frank

// clang++ prog.cc -Wall -Wextra -std=c++17

 #include <iostream>
 #include <vector>

 namespace dbj {
    template<class T>
      struct is_vector {
        using type = T ;
        constexpr static bool value = false;
   };

   template<class T>
      struct is_vector<std::vector<T>> {
        using type = std::vector<T> ;
        constexpr  static bool value = true;
   };

  // and the two "olbigatory" aliases
  template< typename T>
     inline constexpr bool is_vector_v = is_vector<T>::value ;

 template< typename T>
    using is_vector_t = typename is_vector<T>::type ;

 } // dbj

   int main()
{
   using namespace dbj;
     std::cout << std::boolalpha;
     std::cout << is_vector_v<std::vector<int>> << std::endl ;
     std::cout << is_vector_v<int> << std::endl ;
}   /*  Created 2018 by dbj@dbj.org  */

"la preuve du pudding". Il y a de meilleures façons de le faire, mais cela fonctionne pour std::vector.

0
répondu 2018-06-27 08:34:49