Ste*_*ing 18 c++ for-loop template-meta-programming
是否有可能存在这样的事情?
template<int Channel>
void deduce_mask(Matrix const &src, int mask[])
{
//I hope i could become a constant and the compiler would unroll the loop at compile time
for(int i = Channel; i != -1; --i)
{
//mapper is a helper class which translate two and three dimension into one dimension index
//constexpr makes it possible to find out the index at compile time
mask[mapper(0, 1, i)] = src(row - 1, col)[i];
mask[mapper(1, 1, i)] = src(row, col)[i];
mask[mapper(2, 1, i)] = src(row + 1, col)[i];
}
}
Run Code Online (Sandbox Code Playgroud)
代替
template<int Channel>
class deduceMask
{
public:
static void deduce_mask(matrix const &src, int mask[]);
};
template<int Channel>
void deduce_mask(matrix const &src, int mask[])
{
mask[mapper(0, 1, Channel)] = src(row - 1, col)[Channel];
mask[mapper(1, 1, Channel)] = src(row, col)[Channel];
mask[mapper(2, 1, Channel)] = src(row + 1, col)[Channel];
deduceMask<Channel - 1>::deduce_mask(src, mask);
}
template<>
class deduceMask<-1>
{
public:
static void deduce_mask(matrix const &src, int mask[])
{
}
};
Run Code Online (Sandbox Code Playgroud)
当我希望编译器在编译时找出结果时,第二个解决方案是我能想到的唯一解决方案.在元编程解决方案中,我是否有一种简单的方法可以使"i"成为恒定值?对我来说,一个简单的for循环比使用元编程版本更容易使用.
leg*_*ego 21
C++中的模板元编程是纯函数式编程,在纯函数式编程中,你不能使用像for或while这样的循环,你根本就没有任何可变数据.你只有递归.为了使递归更容易,您需要提高抽象级别.您拥有的递归代码很好,但迭代和工作可以分开:
template <int First, int Last>
struct static_for
{
template <typename Fn>
void operator()(Fn const& fn) const
{
if (First < Last)
{
fn(First);
static_for<First+1, Last>()(fn);
}
}
};
template <int N>
struct static_for<N, N>
{
template <typename Fn>
void operator()(Fn const& fn) const
{ }
};
Run Code Online (Sandbox Code Playgroud)
现在您已经拥有了这个元函数,您可以像这样编写deduce_mask函数:
template<int Channel>
void deduce_mask(Matrix const &src, int mask[])
{
static_for<0, Channel>()([&](int i)
{
mask[mapper(0, 1, i)] = src(row - 1, col)[i];
mask[mapper(1, 1, i)] = src(row, col)[i];
mask[mapper(2, 1, i)] = src(row + 1, col)[i];
});
}
Run Code Online (Sandbox Code Playgroud)
带有/ Ob1命令行开关的Visual C++ 2012将此代码编译为:
push 0
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 1
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 2
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 3
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 4
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
...
Run Code Online (Sandbox Code Playgroud)
如果你不能使用lambda函数,你需要编写一个仿函数.Functor比lambda函数有一个优势 - 你可以指定一个调用约定(如果你不介意这样做).如果仿函数的operator()具有__fastcall调用约定,那么您将看到mov edx, x而不是push x汇编代码.
随着if constexpr我们可以提高AOK的解决方案.
template <int First, int Last, typename Lambda>
inline void static_for(Lambda const& f)
{
if constexpr (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last>(f);
}
}
Run Code Online (Sandbox Code Playgroud)
有了这个,我们可以摆脱它 ::apply
static_for<0, Channel>([&](auto i)
{
// code...
mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change
std::get<i.value>(some_tuple); // But here you must get the member .value
// more code...
});
Run Code Online (Sandbox Code Playgroud)
不幸的是你还是要写i.value.
请注意,如果没有if constexprAOK的方式需要部分模板特化,这是不可能的static_for.
乐高的响应虽然优雅而令人敬畏,但如果您希望索引进入模板,则不会编译 - 例如 std::get<i>(some_tuple)
如果您想在将来实现此附加功能,以下代码将起作用,并且应该向后兼容乐高的解决方案(除了我使用静态应用方法而不是 operator()):
template <int First, int Last>
struct static_for
{
template <typename Lambda>
static inline constexpr void apply(Lambda const& f)
{
if (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last>::apply(f);
}
}
};
template <int N>
struct static_for<N, N>
{
template <typename Lambda>
static inline constexpr void apply(Lambda const& f) {}
};
Run Code Online (Sandbox Code Playgroud)
现在您可以执行以下操作:
static_for<0, Channel>::apply([&](auto i) // Changed from '(int i)'. In general, 'auto' will be a better choice for metaprogramming!
{
// code...
mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change
std::get<i.value>(some_tuple); // But here you must get the member .value
// more code...
});
Run Code Online (Sandbox Code Playgroud)
在 VC++ 2015 中进行了测试。我没有研究为什么会这样,但我只能假设它std::integral_constant<T,...>定义了一个隐式转换为Tusing 值,但是编译器无法确定隐式转换会产生 a constexpr,因此您必须检索该值使用i.value,这是一个constexpr.
在评论中解决@tom 的问题 如果要迭代参数包,可以执行以下操作(相同的实现):
template<typename... Args>
inline constexpr auto foo(const Args&... args)
{
static_for<0,sizeof...(Args)>::apply([&](auto N)
{
std::cout << std::get<N.value>(std::make_tuple(args...));
});
}
foo(1,"a",2.5); // This does exactly what you think it would do
Run Code Online (Sandbox Code Playgroud)
如果std::get<N.value>(std::make_tuple(args...))看起来很难看,您可以创建另一个constexpr最小化代码的函数。