How to detect whether a type is std::tuple or not?
Asked Answered
G

3

15

Why I have strange output for this code? How to test for a type in the right way?

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

I use mingw64-gcc 7.2.0 with -std=c++17, I got output "01" Why I got two different output? Aren't they the same type?

Gluck answered 7/9, 2017 at 10:2 Comment(1)
You might use something like template <typename> struct Dummy; template struct Dummy<decltype(ItemTemplate::traits)>; to let compiler gives you the type in error message.Aspergillosis
P
13

Note that the type of ItemTemplate::traits (i.e. decltype(ItemTemplate::traits)) is const std::tuple<TraitBlock, TraitRock>, which doesn't match the specified type (i.e. std::tuple<Ts...>) in the specialization of is_tuple.

You can remove the const-ness by std::remove_const, e.g.

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

or add another specialization for const (and might volatile as well):

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 {};
Peyton answered 7/9, 2017 at 10:18 Comment(0)
A
16

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

So you have to handle cv qualifier somewhere.

Aspergillosis answered 7/9, 2017 at 10:7 Comment(2)
Oh, constexpr implies const, but When I use constexpr, will it also make inner types of make_tuple const?Disgust
Not sure what you mean, but you have const std::tuple<TraitBlock, TraitRock>, not const std::tuple<const TraitBlock, const TraitRock>.Aspergillosis
P
13

Note that the type of ItemTemplate::traits (i.e. decltype(ItemTemplate::traits)) is const std::tuple<TraitBlock, TraitRock>, which doesn't match the specified type (i.e. std::tuple<Ts...>) in the specialization of is_tuple.

You can remove the const-ness by std::remove_const, e.g.

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

or add another specialization for const (and might volatile as well):

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 {};
Peyton answered 7/9, 2017 at 10:18 Comment(0)
D
9

You need to remove all qualifiers. Instead of doing this all yourself you should use std::decay_t which removes all qualifiers for you and dispatch to your trait. For instance

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>> {}
Daffodil answered 7/9, 2017 at 11:37 Comment(3)
don't you mean std::remove_cv_t instead of std::decay_t?Sexcentenary
It depends, remove_cv_t is a subset of decay_t. The latter will as well remove references (r- and l-values) which is usually required for traits as well. Unless you don't want to find std::tuple<…>& and std::tuple<…>&&.Tetrode
Typically, we don't consider std::tuple<>& to be a tuple. That's the usual way traits work.Henton

© 2022 - 2024 — McMap. All rights reserved.