c++ 是什么导致了非类型模板参数的部分特殊化的不同行为?

vwoqyblh  于 2023-10-21  发布在  其他
关注(0)|答案(1)|浏览(81)

我正在尝试完成一个模板元编程练习text。然而,当我试图部分专门化一个非类型模板参数时,我遇到了不同的行为。
我定义了一个编译时向量。

template <int... Nums>
struct Vector {};

然后,我定义了一个关于“Vector”的“Get”操作,代码可以编译。

template <size_t Idx, typename Vec>
struct Get;

template <int Head, int... Tail>
struct Get<0, Vector<Head, Tail...>> // partial specialize the "Idx"
{
    static constexpr int value = Head;
};

template <size_t Idx, int Head, int... Tail>
struct Get<Idx, Vector<Head, Tail...>>
{
    static_assert(Idx > 0, "Out of Vector bound");
    static constexpr int value = Get<Idx - 1, Vector<Tail...>>::value;
};

但是,当我尝试像下面这样定义“插入”操作时,VS2022中的代码编译失败,错误代码为“C2752多个部分专门化匹配”。

template <size_t Pos, int Val, typename Vec>
struct Insert;

template <size_t Pos, int Val, typename Vec>
using InsertT = typename Insert<Pos, Val, Vec>::type;

template <int Val, int... Elems>
struct Insert<0, Val, Vector<Elems...>> // partial specialize the "Pos"
{
    using type = Vector<Val, Elems...>;
};

template <size_t Pos, int Val, int Head, int... Tail>
struct Insert<Pos, Val, Vector<Head, Tail...>>
{
    using type = PrependT<Head, InsertT<Pos - 1, Val, Vector<Tail...>>>; // push an element in the front of the vector
};

最新消息:

#include <type_traits>

template <int... Nums>
struct Vector {};

template <int, typename>
struct Prepend;

template <int Ele, typename Vec>
using PrependT = typename Prepend<Ele, Vec>::type;

template <int Ele, int... Nums>
struct Prepend<Ele, Vector<Nums...>>
{
    using type = Vector<Ele, Nums...>;
};

template <size_t Idx, typename Vec>
struct Get;

template <int Head, int... Tail>
struct Get<0, Vector<Head, Tail...>> // partial specialize the "Idx"
{
    static constexpr int value = Head;
};

template <size_t Idx, int Head, int... Tail>
struct Get<Idx, Vector<Head, Tail...>>
{
    static_assert(Idx > 0, "Out of Vector bound");
    static constexpr int value = Get<Idx - 1, Vector<Tail...>>::value;
};

template <size_t Pos, int Val, typename Vec>
struct Insert;

template <size_t Pos, int Val, typename Vec>
using InsertT = typename Insert<Pos, Val, Vec>::type;

template <int Val, int... Elems>
struct Insert<0, Val, Vector<Elems...>> // partial specialize the "Pos"
{
    using type = Vector<Val, Elems...>;
};

template <size_t Pos, int Val, int Head, int... Tail>
struct Insert<Pos, Val, Vector<Head, Tail...>>
{
    using type = PrependT<Head, InsertT<Pos - 1, Val, Vector<Tail...>>>; // push an element in the front of the vector
};

int main()
{
    // test Vector
    static_assert(std::is_same_v<Vector<1, 2>, Vector<1, 2>>);

    // test Prepend
    static_assert(std::is_same_v<Prepend<1, Vector<2, 3>>::type, Vector<1, 2, 3>>);

    // test Get
    static_assert(Get<0, Vector<0, 1, 2>>::value == 0);
    static_assert(Get<1, Vector<0, 1, 2>>::value == 1);
    static_assert(Get<2, Vector<0, 1, 2>>::value == 2);

    // test Insert
    static_assert(std::is_same_v<Insert<0, 3, Vector<4, 5, 6>>::type, Vector<3, 4, 5, 6>>);
    static_assert(std::is_same_v<Insert<1, 3, Vector<4, 5, 6>>::type, Vector<4, 3, 5, 6>>);
    static_assert(std::is_same_v<Insert<2, 3, Vector<4, 5, 6>>::type, Vector<4, 5, 3, 6>>);
    static_assert(std::is_same_v<Insert<3, 3, Vector<4, 5, 6>>::type, Vector<4, 5, 6, 3>>);
}

在我看来,两种操作中的部分专门化是相同的。为什么一个编译成功,而另一个编译失败?

mpbci0fu

mpbci0fu1#

正如@Igor Tandetnik所提到的,原始代码不起作用的原因是Insert<0, Val, Vector<Elems...>>在第一个参数上比Insert<Pos, Val, Vector<Head, Tail...>>更专业,但Vector<Elems...>Vector<Head, Tail...>更不专业。**因此,没有一个专业化比另一个专业化更专业化。
基于上述事实,Igor Tandetnik提出了以下解决方案。这个解决方案主要有两个修改,一方面,将Vector<Elems...>改为Vector<Head, Tail...>,这样Insert<0, Val, Vector<Elems...>>Insert<Pos, Val, Vector<Head, Tail...>>Pos中只会有差异。另一方面,增加了一个专门化,以处理在Pos=0处将Val添加到空Vector的情况。

template <size_t Pos, int Val, typename Vec>
struct Insert;

template <size_t Pos, int Val, typename Vec>
using InsertT = typename Insert<Pos, Val, Vec>::type;

template <int Val>
struct Insert<0, Val, Vector<>> // Insert element at 0 into an empty vector.
{
    using type = Vector<Val>;
};

template <int Val, int Head, int... Tail>
struct Insert<0, Val, Vector<Head, Tail...>>  // Insert element at 0 into non-empty vector.
{
    using type = Vector<Val, Head, Tail...>;
};

template <size_t Pos, int Val, int Head, int... Tail>
struct Insert<Pos, Val, Vector<Head, Tail...>>
{
    using type = PrependT<Head, InsertT<Pos - 1, Val, Vector<Tail...>>>; // push an element in the front of the vector
};

@Bob__给出了另一个基于C++20的requires的解决方案。requires描述了对Pos参数的约束。

template <size_t Pos, int Val, typename Vec>
struct Insert;

template <size_t Pos, int Val, typename Vec>
using InsertT = typename Insert<Pos, Val, Vec>::type;

template <int Val, int... Elems>
struct Insert<0ull, Val, Vector<Elems...>> // partial specialize the "Pos"
{
    using type = Vector<Val, Elems...>;
};

template <size_t Pos, int Val, int Head, int... Tail>
  requires (Pos > 0)                                       // <-----
struct Insert<Pos, Val, Vector<Head, Tail...>>
{
    using type = PrependT<Head, InsertT<Pos - 1, Val, Vector<Tail...>>>; // push an element in the front of the vector
};

相关问题