【问题标题】:Why does MSVC fail to compile this CRTP code?为什么 MSVC 无法编译此 CRTP 代码?
【发布时间】:2020-11-23 23:13:41
【问题描述】:

我编写了一些代码,在最新版本的 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 的其他值?
  • 为什么 list_impl 还是需要 TL?
  • @n。 '代词' m。是的,这个例子并不完整,只是显示了一个错误。完整的实现包含很多方法如push_back/push_front/.. 并使用TL。这里的主要思想是空列表没有backfront 成员,但应该重用其他逻辑。看看更详细的版本(带有push_back):pastebin.com/snRC0EPi

标签: c++ templates


【解决方案1】:

这似乎确实是错误。要引用当前实例化,您通常可以省略当前类型的模板参数。这就是 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 &lt;...&gt;,而使用 我不能这样做。在某些情况下我需要它。您可以在详细示例中看到它是如何使用的:pastebin.com/snRC0EPi
  • 您更新的代码(使用GetTemplateOfVariadicType)很可能会正常工作,但我想避免这样的黑客攻击。无论如何,谢谢你的帮助
  • 我不确定您是否确切地看到了错误出现的位置。它不是基类,它是 using 子句(impl ...)。那里 MSVC 使用绑定类型。
  • 这里你可以找到一个带有模板模板参数但修改了 using 子句的版本:godbolt.org/z/nh1dYY
  • 哦,我不知道 using 子句里面有绑定类型而不是模板。然后我可以在没有帮助的情况下采用这样的解决方案:godbolt.org/z/asYGYf
猜你喜欢
  • 2012-12-22
  • 2012-05-28
  • 2010-10-16
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
相关资源
最近更新 更多