Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does MSVC fail to compile this CRTP code?

Tags:

c++

templates

I wrote some code that compiles well on recent versions of GCC and Clang, but fails on MSVC:

invalid template argument for template parameter 'TL', expected a class template

Can anyone please explain is this a bug or have I misunderstood something?

Without partial specialization of types_list it works fine on MSVC too.

#include <cstdint>
#include <type_traits>

namespace detail
{
    template <std::size_t Index, typename ...Ts>
    struct get
    {
        static_assert(Index < sizeof...(Ts), "types_list::get index out of bounds");

    private:
        template <std::size_t CurrentIndex, typename ...Us>
        struct helper
        {
            using type = void;
        };

        template <std::size_t CurrentIndex, typename U, typename ...Us>
        struct helper<CurrentIndex, U, Us...>
        {
            using type = std::conditional_t<CurrentIndex == Index, U, typename helper<CurrentIndex + 1, Us...>::type>;
        };

    public:
        using type = typename helper<0, Ts...>::type;
    };

    template <template <typename...> typename TL, typename ...Ts>
    struct list_impl
    {
        inline static constexpr std::size_t size = sizeof...(Ts);

        template <std::size_t Index>
        using get = typename detail::get<Index, Ts...>::type;
    };
}

template <typename ...Ts>
struct types_list : public detail::list_impl<types_list, Ts...>
{
};

template <typename T, typename ...Ts>
struct types_list<T, Ts...> : public detail::list_impl<types_list, T, Ts...>
{
private:
    using impl = detail::list_impl<types_list, T, Ts...>;

public:
    using front = typename impl:: template get<0>;
    using back = typename impl:: template get<impl::size - 1>;
};

using t = types_list<int, double>::front;
using t2 = types_list<int, double>::back;
using t3 = types_list<int, char, double>::get<1>;

int main()
{
    t x = 10;
    t2 y = 1.4;
    t3 z = 'a';
}

EDIT: More detailed example https://pastebin.com/snRC0EPi

like image 529
d_kog Avatar asked Nov 23 '20 23:11

d_kog


1 Answers

It seems to be bug indeed. To refer to the current instantiation you can usually omit the template paramter of the current type. This is what MSVC seems to do here. It causes an error because the template expects a template template parameter.

But why you want to use a template template parameter? For CRTP you use the "bound template type".


Update

If you need to instantiate the template with new parameters this can be done easily with a helper type:

namespace helper{
    template<
        typename VariadicType
    >
    class GetTemplateOfVariadicType{
    };

    template<
        template <typename...> typename Template,
        typename... Ts
    >
    class GetTemplateOfVariadicType<Template<Ts...>>
    {
    public:
        template<typename... T>
        using type = Template<T...>;
    };
}

Usage:

using OtherTL = typename ::helper::GetTemplateOfVariadicType<TL>::template type<int, bool, char>;

See here: godbolt

like image 149
Bernd Avatar answered Sep 18 '22 14:09

Bernd