2017-09-07 8 views
14

Pourquoi j'ai une sortie étrange pour ce code? Comment tester un type dans le bon sens?Comment détecter si un type est std :: tuple ou non?

#include <iostream> 
#include <tuple> 
#include <type_traits> 

template<typename T> struct is_tuple : std::false_type {}; 
template<typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type {}; 

struct TraitBlock { 
    using BlockLocation = struct { std::uint64_t x, y, z; }; 
}; 

struct TraitRock {}; 

struct ItemTemplate{ 
    static constexpr auto traits = std::make_tuple(
     TraitBlock{}, 
     TraitRock{} 
    ); 
}; 

int main(){ 
    using A = std::tuple<char, int,double,char>; 
    std::cout << is_tuple<decltype(ItemTemplate::traits)>::value 
    << is_tuple<decltype(std::make_tuple(
     TraitBlock{}, 
     TraitRock{} 
    ))>::value 
    << std::endl; 
} 

J'utilise mingw64-gcc 7.2.0 avec std = C++ 17, je me suis sortie "01" Pourquoi je suis arrivé deux sorties différentes? Ne sont-ils pas du même type?

+3

Vous pouvez utiliser quelque chose comme 'template struct Dummy; template struct Dummy ; 'laisser le compilateur vous donner le type dans le message d'erreur. – Jarod42

Répondre

16

decltype(ItemTemplate::traits) est const std::tuple<TraitBlock, TraitRock>.

Vous devez donc gérer le qualificatif cv quelque part.

+0

Oh, constexpr implique const, mais quand j'utilise constexpr, va-t-il aussi faire des types internes de make_tuple const? –

+1

Vous ne savez pas ce que vous voulez dire, mais vous avez 'const std :: tuple ', pas 'const std :: tuple '. – Jarod42

12

On notera que le type de ItemTemplate::traits (à savoir decltype(ItemTemplate::traits)) est const std::tuple<TraitBlock, TraitRock>, ce qui ne correspond pas au type spécifié (à savoir std::tuple<Ts...>) dans la spécialisation de is_tuple. Vous pouvez supprimer la constance par std::remove_const, par exemple.

std::cout << is_tuple<std::remove_const_t<decltype(ItemTemplate::traits)>>::value; 

ou ajouter une autre spécialisation pour const (et pourrait volatile aussi):

template<typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type {}; 
template<typename... Ts> struct is_tuple<const std::tuple<Ts...>> : std::true_type {}; 
template<typename... Ts> struct is_tuple<volatile std::tuple<Ts...>> : std::true_type {}; 
template<typename... Ts> struct is_tuple<const volatile std::tuple<Ts...>> : std::true_type {}; 
7

Vous devez supprimer tous les qualificatifs. Au lieu de faire tout cela vous-même, vous devez utiliser std::decay_t qui supprime tous les qualificatifs pour vous et l'envoi à votre trait. Par exemple

template<typename T> 
struct is_tuple_impl : std::false_type {}; 

template<typename... Ts> 
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {}; 

template<typename T> 
struct is_tuple : is_tuple_impl<std::decay_t<T>> {} 
+1

ne voulez-vous pas dire 'std :: remove_cv_t' au lieu de' std :: decay_t'? – Caleth

+3

Cela dépend, 'remove_cv_t' est un sous-ensemble de' decay_t'. Ce dernier supprimera également les références (valeurs r et l) qui sont généralement requises pour les caractères. Sauf si vous ne voulez pas trouver 'std :: tuple <…> &' et 'std :: tuple <…> &&'. –

+0

En règle générale, nous ne considérons pas 'std :: tuple <> &' comme un tuple. C'est comme ça que les traits fonctionnent. – Barry