我正在完成这篇文章中的练习https://www.slamecka.cz/posts/2021-03-17-cpp-metaprogramming-exercises-1/
首先,我要向作者表示衷心的感谢。这些问题非常有趣、具有挑战性且引人入胜。
问题的一部分给我带来了一些问题,我不太明白为什么。
/**
* 18. Define Insert for Vector, it should take position, value and vector.
* Don't worry about bounds.
* Hint: use the enable_if trick, e.g.
* template<typename X, typename Enable = void> struct Foo;
* template<typename X> struct<std::enable_if_t<..something about X..>> Foo {...};
* template<typename X> struct<std::enable_if_t<..something else about X..>> Foo {...};
*/
// Your code goes here:
// ^ Your code goes here
// 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>>);
我开始解决这个问题,无视作者的提示。
这是我第一次尝试。
template <int P, int I, typename V> struct Insert;
template <int P, int I, int F, int... N> struct Insert<P, I, Vector<F, N...>> {
using type = Prepend<F, typename Insert<P - 1, I, Vector<N...>>::type>::type;
};
template <int I, int... N> struct Insert<0, I, Vector<N...>> {
using type = typename Prepend<I, Vector<N...>>::type;
};
作为此方法的错误消息,编译器抱怨歧义性
E:\problems\main.cpp(212,20): error: ambiguous partial specializations of 'Insert<0, 3, (anonymous namespace)::Vector<4, 5, 6>>'
[build] 212 | std::is_same_v<Insert<0, 3, Vector<4, 5, 6>>::type, Vector<3, 4, 5, 6>>);
[build] | ^
[build] E:\problems\main.cpp(163,49): note: partial specialization matches [with P = 0, I = 3, F = 4, N = <5, 6>]
[build] 163 | template <int P, int I, int F, int... N> struct Insert<P, I, Vector<F, N...>> {
[build] | ^
[build] E:\problems\main.cpp(167,35): note: partial specialization matches [with I = 3, N = <4, 5, 6>]
[build] 167 | template <int I, int... N> struct Insert<0, I, Vector<N...>>
std::conditional_t
会怎样?
template <int P, int I, typename V> struct Insert;
template <int P, int I, int F, int... N> struct Insert<P, I, Vector<F, N...>> {
using type = std::conditional_t<
P == 0, typename Prepend<I, Vector<N...>>::type,
typename Prepend<F, typename Insert<P - 1, I, Vector<N...>>::type>::type>;
};
这次我收到了不同的错误消息
[build] E:\problems\main.cpp(178,36): error: implicit instantiation of undefined template '(anonymous namespace)::Insert<-3, 3, (anonymous namespace)::Vector<>>'
[build] 178 | typename Prepend<F, typename Insert<P - 1, I, Vector<N...>>::type>::type>;
[build] | ^
[build] E:\problems\main.cpp(178,36): note: in instantiation of template class '(anonymous namespace)::Insert<-2, 3, (anonymous namespace)::Vector<6>>' requested here
[build] E:\problems\main.cpp(178,36): note: in instantiation of template class '(anonymous namespace)::Insert<-1, 3, (anonymous namespace)::Vector<5, 6>>' requested here
[build] E:\problems\main.cpp(222,20): note: in instantiation of template class '(anonymous namespace)::Insert<0, 3, (anonymous namespace)::Vector<4, 5, 6>>' requested here
[build] 222 | std::is_same_v<Insert<0, 3, Vector<4, 5, 6>>::type, Vector<3, 4, 5, 6>>);
[build] | ^
[build] E:\problems\main.cpp(173,44): note: template is declared here
[build] 173 | template <int P, int I, typename V> struct Insert;
[build] | ^
任何人都可以解释一下为什么我收到这些消息,它们为什么不同?
代码失败是因为模板专业化一般太宽泛。你无法阻止这个
template <int I, int... N>
struct Insert<0, I, Vector<N...>> {
using type = typename Prepend<I, Vector<N...>>::type;
};
还有这个
template <int P, int I, int F, int... N>
struct Insert<P, I, Vector<F, N...>> {
using type = typename Prepend<F, typename Insert<P - 1, I, Vector<N...>>::type>::type;
};
来自匹配
P = 0
,即使第二个代码用于递归。如果 P = 0
那么 P = 0
。你必须手动处理它。
对此的一个适当的解决方案是使用
std::enable_if
来防止递归专门化在最终达到 P == 0
的情况下与
P = 0
匹配。
// forward declaration of Insert with std::enable_if
template <int P, int I, typename V, typename Enable = void>
struct Insert;
// specialization for P > 0 for recursion
template <int P, int I, int F, int... N>
struct Insert<P, I, Vector<F, N...>, std::enable_if_t<(P > 0)>> {
using type = typename Prepend<F, typename Insert<P - 1, I, Vector<N...>>::type>::type;
};