4

I was trying to static_assert that some meta transformer algorithm worked, and it incredibly did not compare to same, even though the typeid().name() returned the exact same string.

A repetition of the type expression in the typedef could fix the is_same, but I can't understand how repeating the initializer expression in the typedef changes the type, over taking the decltype of an auto variable initialized with that same expression.

A more concrete explanation of what I was doing:
I did a meta transformer that can transform a meta value list (containing enumerators) to a std::tuple of all enumerators. Then I checked that the tuple generated was the one I expected.

all code hereunder, check the line with the comments // works // doesn't work
And the final test is at the end.

#include <type_traits>
#include <tuple>

template<typename T> struct ValueListAsTuple;  // master template
// destructurer of list:
template<template <auto...> class L, auto... Vs>
struct ValueListAsTuple<L<Vs...>>
{
    static constexpr auto value = std::make_tuple(Vs...);
    //using type = decltype(std::make_tuple(Vs...)); // works
    using type = decltype(value); // doesn't work
};

// template typedef
template<typename T> using ValueListAsTuple_t = typename ValueListAsTuple<T>::type;

struct Kind
{
    enum EnumType { E1, E2 };
    template <auto... Values> struct MetaVals{};  // meta value list
    using MetaValueList = MetaVals<
        E1,
        E2
    >;
};

int main(int argc, const char* argv[])
{
    auto tuple = ValueListAsTuple_t< Kind::MetaValueList > {};

    //std::cout << typeid(tuple).name() << '\n';
    // this prints: "class std::tuple<enum Kind::EnumType, enum Kind::EnumType>"

    // manual re-creation of the type, for testing:
    std::tuple< Kind::EnumType, Kind::EnumType > t2;

    //std::cout << typeid(t2).name() << '\n';
    // this prints the exact same thing.

    static_assert( std::is_same_v< std::tuple_element<0, decltype(tuple)>::type, Kind::EnumType > );
    static_assert( std::is_same_v< std::tuple_element<1, decltype(tuple)>::type, Kind::EnumType > );

    // WHAT ???
    static_assert( std::is_same_v< 
                        ValueListAsTuple_t< Kind::MetaValueList >,
                        std::tuple< Kind::EnumType, Kind::EnumType >
                                 > );
    // is_convertible_v works but I don't care ! wth ?
}

So as you can see, is_same could not deduce the same type when the type was declared through value but it worked if I repeat std::make_tuple(... I can't see where there is any difference between the 2 forms.

I even checked element by element that the types are the same at each index of the tuple.

Check that in action in godbolt:
https://godbolt.org/z/QUCXMB

The behavior is the same across gcc/clang/msvc

v.oddou
  • 6,476
  • 3
  • 32
  • 63
  • 1
    Not to be rude, but is this really the most minimal piece of code you can write to reproduce the issue? There's a fair bit of noise here for a [mcve]. – StoryTeller - Unslander Monica Mar 20 '19 at 08:53
  • @StoryTeller no no you're right, I tried to trim a FAIR lot from the original code. It took a while to get to this concision actually. Though I agree with you, I'm sure a second iteration could get it even smaller :) – v.oddou Mar 20 '19 at 08:54
  • 2
    @StoryTeller I went and trimmed it 5 or 6 lines :)) I hope it helps future googlers ! – v.oddou Mar 20 '19 at 09:00

1 Answers1

10

It's a trivial error, really, but hard to spot unfortunately. The issue you face is because constexpr implies const. So in your example type is const tuple<...>, which is not the same type as the non cv-qualified type you check. A short fix to the alias should make your test pass:

using type = std::remove_const_t<decltype(value)>;
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • omg I see... I tried checking for that though, using the very limited tools we have for compile times debugging: intellisense gives tooltips that shows the expanded type, and `const` was not appearing. the `typeid` also failed to reveal the const too :'( – v.oddou Mar 20 '19 at 09:03
  • 7
    @v.oddou - There's a trick I favor for this, which I learnt from Scott Meyers' book. You use a template like `template struct wtf;` (undefined). Then you try to create an object `wtf o;` and the compiler's complaints about the incomplete type will show exactly what it put in as the type argument. – StoryTeller - Unslander Monica Mar 20 '19 at 09:04
  • Gee, silly me thinking `template struct print;` is a descriptive name. – Passer By Mar 20 '19 at 10:57
  • 1
    @PasserBy - It is, but if I find myself in need of it then `wtf_is` feels much more appropriate. – StoryTeller - Unslander Monica Mar 20 '19 at 11:48