std::is_sameとmpl::equal

結論

Twitterで何人かの方に教えていただきました。
typeidがトップレベルのcv修飾や参照を無視するので、それをデマングルするとその部分だけが違う型は同じように表示されていました。

    template<template<class...> class Type, class... Args>
    static const boost::mpl::vector<T, Args...> tp_type_impl(Type<Args...>); // constが付く
    
    template<class Type>
    static const boost::mpl::vector<Type> tp_type_impl(Type); // constが付く

試しに以下のようにしてみたらtrueになりましたとさ…。

    template<template<class...> class Type, class... Args>
    static boost::mpl::vector<T, Args...> tp_type_impl(Type<Args...>);
    
    template<class Type>
    static boost::mpl::vector<Type> tp_type_impl(Type);

話題

昨日のコード、よくよく考えたら型だけが必要でインスタンス化とか要らないんじゃないかということで削ってみたらstatic_assertを通れなくなりました。
おかしいなと思いmpl::equalで試したら通りました。

std::is_sameとmpl::equalの挙動が異なるというのは知っていましたがこれは…。

mplライブラリの中で同じように扱えるものの、内部的に違う型だった場合の比較結果がis_sameだとfalseになるというのは分かりますが、デマングルしてみて完全に型が一致していた場合もfalseという結果を返してくれるというのはどういうことですか…?
std::is_sameは厳密には何を比較しているんでしょう。

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/equal.hpp>
#include <type_traits>

template<class... Types>
struct Foo {};

template<class T>
struct tp_type
{
private:
    template<template<class...> class Type, class... Args>
    static const boost::mpl::vector<T, Args...> tp_type_impl(Type<Args...>);
    
    template<class Type>
    static const boost::mpl::vector<Type> tp_type_impl(Type);
    
public:
    using type = decltype(tp_type_impl(T())); // T()はTの実体が無い型だとエラーになる
};

int main()
{
    using type = tp_type<Foo<Foo<int>, double, char>>::type;
    using test = boost::mpl::vector<Foo<Foo<int>, double, char>, Foo<int>, double, char>;
    
    int status;
    
    std::cout << abi::__cxa_demangle(typeid(type).name(), 0, 0, &status) << std::endl;
    std::cout << abi::__cxa_demangle(typeid(test).name(), 0, 0, &status) << std::endl;

    std::cout << std::boolalpha << boost::mpl::equal<type, test>::type::value << std::endl;
    std::cout << std::boolalpha << std::is_same<type, test>::value << std::endl;
}

出力

boost::mpl::vector<Foo<Foo<int>, double, char>, Foo<int>, double, char, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na>
boost::mpl::vector<Foo<Foo<int>, double, char>, Foo<int>, double, char, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na, mpl::na>
true
false

追記

is_sameとかとは関係無いのですが、上の定義だとFooの実体が存在しない場合にT()がコンパイルエラーとなる為、メタ関数自体が使えない事が分かりました。
回避策としてtype_holderをワンクッション挟めば実体の無い型に対してもうまくいきました。

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/equal.hpp>
#include <type_traits>

template<class... Types>
struct Foo; // 実体の無い型

template<class T>
struct tp_type
{
private:
    template<class>
    struct type_holder {}; // 型を渡すための保持クラス
    
    template<template<class...> class Type, class... Args>
    static const boost::mpl::vector<T, Args...> tp_type_impl(type_holder<Type<Args...>>); // type_holderを介して受け取る
    
    template<class Type>
    static const boost::mpl::vector<Type> tp_type_impl(Type);
    
public:
    using type = decltype(tp_type_impl(type_holder<T>())); // type_holderのインスタンス化に失敗することは無い
};

int main()
{
    using type = tp_type<Foo<Foo<int>, double, char>>::type;
    using test = boost::mpl::vector<Foo<Foo<int>, double, char>, Foo<int>, double, char>;
    
    int status;
    
    std::cout << abi::__cxa_demangle(typeid(type).name(), 0, 0, &status) << std::endl;
    std::cout << abi::__cxa_demangle(typeid(test).name(), 0, 0, &status) << std::endl;

    std::cout << std::boolalpha << boost::mpl::equal<type, test>::type::value << std::endl;
    std::cout << std::boolalpha << std::is_same<type, test>::value << std::endl;
}