为什么 MSVC 无法编译这个 CRTP 代码?

6
我写了一些代码,在最新版本的GCC和Clang上编译良好,但在MSVC上失败:
无效的模板参数用于模板参数'TL',需要一个类模板。
请问这是个错误还是我理解错了?
如果没有对types_list进行部分特化,则在MSVC上也可以正常工作。
#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';
}

编辑:更详细的示例https://pastebin.com/snRC0EPi


可能这只是一个处理注入类名的歧义性的错误,该类名可以用作类型或模板。也许尝试使用/permissive的其他值? - Davis Herring
1
list_impl为什么需要TL呢? - n. m.
@n. '代词' m. 是的,这个例子不完整,只是展示了一个错误。 完整的实现包含许多方法,如 push_back / push_front / .. 并使用 TL。这里的主要思想是空列表没有 backfront 成员,但其他逻辑应该被重用。请查看更详细的版本(带有 push_back):https://pastebin.com/snRC0EPi - d_kog
1个回答

2

看起来这确实是个 bug。要引用当前实例,通常可以省略当前类型的模板参数。这似乎是 MSVC 在这里所做的。由于该模板需要一个模板模板参数,因此会导致错误。

但是为什么你想使用模板模板参数?对于 CRTP,您使用“绑定模板类型”。


更新

如果您需要使用新参数实例化模板,可以使用辅助类型轻松完成:

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

使用方法:

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

请看这里: godbolt

我正在使用模板模板参数来创建list_impl内的types_list<...>,而使用<typename TL>则无法实现。在某些情况下我需要它。您可以在详细示例中查看它的用法:https://pastebin.com/snRC0EPi - d_kog
你更新后的代码(使用GetTemplateOfVariadicType)很可能能够正常工作,但我想避免这样的hack。无论如何,感谢你的帮助。 - d_kog
我不确定你是否确切地看到了错误发生的位置。它不是基类,而是using子句(impl...)。在那里,MSVC使用了绑定类型。 - Bernd
在这里,您可以找到一个带有模板参数的版本,但是使用子句已经被修改:https://godbolt.org/z/nh1dYY - Bernd
哦,我不知道在 using 子句中有绑定类型而不是模板。那么我可以使用这种解决方案,而不需要帮助程序:https://godbolt.org/z/asYGYf - d_kog
看起来不错... 有了本地别名模板,MSVC就不会混淆了... - Bernd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接