提问人:LChao 提问时间:10/7/2023 最后编辑:LChao 更新时间:10/13/2023 访问量:97
是什么导致了非类型模板参数的部分专用化中的不同行为?
What caused the different behaviors in partial specialization of non-type template argument?
问:
我正在尝试完成模板元编程练习文本。但是,当我尝试部分专用化非类型模板参数时,我遇到了不同的行为。
我定义了一个编译时向量。
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>>);
}
在我看来,两种操作的部分专业化是相同的。为什么一个编译成功,而另一个编译失败?
答:
1赞
LChao
10/13/2023
#1
正如 @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
Val
Vector
Pos=0
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
};
评论
PrependT
Idx
Pos
Get
Insert
template <size_t Val, int... Elems> struct Insert<0, Val, Vector<Elems...>>
Pos
Pos
Get
Idx