我正在研究将T
类型的NxN矩阵旋转90度的问题。本着DRY的精神,我希望我的旋转功能的功能签名看起来像这样:
template <typename T, std::size_t N>
void rotate_90(Matrix<T, N>& m, std::function<void(T&, T&, T&, T&)> swap_direction);
这样我就可以通过传递不同的std::function<void(T&, T&, T&, T&)>
来顺时针和逆时针交换相同的功能。
我目前有以下代码:
#include <iostream>
#include <array>
#include <functional>
template <typename T, std::size_t N>
using Matrix = std::array<std::array<T, N>, N>;
template <typename T>
void four_way_swap_clockwise(T& top_left, T& top_right, T& bottom_left, T& bottom_right) {
T temp = top_left;
top_left = top_right;
top_right = bottom_right;
bottom_right = bottom_left;
bottom_left = temp;
}
template <typename T, std::size_t N>
void rotate_90(Matrix<T, N>& m, std::function<void(T&, T&, T&, T&)> swap_direction) {
for(std::size_t i = 0; i < N/2; ++i) {
for(std::size_t j = 0; j < (N+1)/2; ++j) {
swap_direction(
m[i][j],
m[N-j-1][i],
m[j][N-i-1],
m[N-i-1][N-j-1]
);
}
}
}
int main() {
constexpr std::size_t N = 5;
Matrix<int, N> m {{
{{1,2,3,4,5}},
{{6,7,8,9,10}},
{{11,12,13,14,15}},
{{16,17,18,19,20}},
{{21,22,23,24,25}}
}};
std::function<void(int&, int&, int&, int&)> swap_clockwise(four_way_swap_clockwise);
rotate_90(m, swap_clockwise);
}
目前无法编译,但失败并出现以下错误:
error: no matching function for call to 'std::function<void(int&, int&, int&, int&)>::function(<unresolved overloaded function type>)'
std::function<void(int&, int&, int&, int&)> swap_clockwise(four_way_swap_clockwise);
然而,即使它确实编译了,它也违背了模板编程的目的,以指定交换函数的参数类型的类型(即,在std::function<void(int&, int&, int&, int&)> swap_clockwise(four_way_swap_clockwise);
的定义中)。
如何通过推导出的模板类型传递std::function
?
答案 0 :(得分:4)
您可能希望rotate_90
更加通用,如下所示:
template <typename T, std::size_t N, typename F>
void rotate_90(Matrix<T, N>& m, F swap_direction) {
for(std::size_t i = 0; i < N/2; ++i) {
for(std::size_t j = 0; j < (N+1)/2; ++j) {
swap_direction(
m[i][j],
m[N-j-1][i],
m[j][N-i-1],
m[N-i-1][N-j-1]
);
}
}
}
答案 1 :(得分:4)
template<class T> struct tag_t{using type=T;};
template<class T> using block_deduction=typename tag_t<T>::type;
此构造阻止C ++尝试从函数参数中推导出模板参数。
template <typename T, std::size_t N>
void rotate_90(Matrix<T, N>& m, block_deduction<std::function<void(T&, T&, T&, T&)>> swap_direction) {
现在第二个参数的类型总是从第一个参数的类型中推断出来。
下一个问题是std::function
没有消除重载函数名称的歧义。重载的函数名称不是C ++值,它是一组名称(在正确的上下文中)找到一个值。 std::function
构造不其中一个上下文。
我们可以使用这样的附加构造函数扩展std::function
:
template<class Sig, class F=std::function<Sig>>
struct my_func:F {
using F::F;
using F::operator=;
my_func( Sig* ptr ):F(ptr) {}
my_func& operator=( Sig* ptr ) {
F::operator=(ptr);
return *this;
}
my_func()=default;
my_func(my_func&&)=default;
my_func(my_func const&)=default;
my_func& operator=(my_func&&)=default;
my_func& operator=(my_func const&)=default;
};
http://philosophy.eserver.org/texts.htm
另一种方法是将重载集包装成lambda:
auto overloads = [](auto&&...args){ return four_way_swap_clockwise(decltype(args)(args)...); };
然后将overloads
传递给您的函数。这个lambda一次代表four_way_swap_clockwise
重载的 all 。
我们也可以通过four_way_swap_clockwise<int>
手动消除歧义。
这两项仍然需要上面的block_deduction
技术。
考虑的另一种选择是:
template <typename T, std::size_t N, class F>
void rotate_90(Matrix<T, N>& m, F&& swap_direction)
我们让swap_direction
完全免费,让算法中出现任何失败。这也略微提升了性能。您仍然需要使用four_way_swap_clockwise
或lambda-wrapper技术消除<int>
的歧义。
另一种方法是让for_way_swap_clockwise
成为一个lambda:
auto four_way_swap_clockwise = [](auto& top_left, auto& top_right, auto& bottom_left, auto& bottom_right) {
auto temp = top_left;
top_left = top_right;
top_right = bottom_right;
bottom_right = bottom_left;
bottom_left = temp;
};
现在它是一个模板operator()
重载的对象。这与block_deduction
解决了您的问题。
简而言之,您的问题有很多方法。
答案 2 :(得分:2)
要调用该函数,
template <typename T, std::size_t N>
void rotate_90(Matrix<T, N>& m, std::function<void(T&, T&, T&, T&)> swap_direction);
下式给出:
template <typename T>
void four_way_swap_clockwise(T& top_left, T& top_right, T& bottom_left, T& bottom_right);
你可以试试这个:
rotate_90<int>(m, four_way_swap_clockwise<int>);
至于为什么你不能这样称呼它:
rotate_90(m, four_way_swap_clockwise);
部分原因是名称four_way_swap_clockwise
是模板功能而不 功能,并且使用此名称需要它的实例化。我实例化为four_way_swap_clockwise<int>
更好的是,根据我对你的问题的第一个评论,最好写一下rotate_90
:
template <typename T, std::size_t N, typename Func>
void rotate_90(Matrix<T, N>& m, Func swap_direction);
答案 3 :(得分:2)
您可以将模板功能变为仿函数:
struct four_way_swap_clockwise {
template <typename T>
void
operator()(T& top_left, T& top_right, T& bottom_left, T& bottom_right) {
T temp = top_left;
top_left = top_right;
top_right = bottom_right;
bottom_right = bottom_left;
bottom_left = temp;
}
};
然后致电:
four_way_swap_clockwise swap_clockwise;
rotate_90(m, swap_clockwise);