我正在编写模板化的短矢量和小矩阵类,它们不限于2-3-4个元素,但可以有任意数量的元素。
template <typename T, size_t N>
class ShortVector
{
public:
...
template <size_t I> T& get() { return m_data[I]; }
template <size_t I> const T& get() const { return m_data[I]; }
private:
T m_data[N];
};
我希望访问接口是静态的,这样我就可以专门使用内置的向量寄存器来支持类的大小。 (可能它们是AVX,C ++ AMP或OpenCL向量。)问题是为这个类编写所有理想的运算符(一元 - ,+, - ,*,/,点,长度......)需要很多模板递归,我甚至没有实现矩阵向量和矩阵 - 矩阵乘法,我将需要嵌套递归。
现在我有非成员朋友操作符和私有成员类,其中包含各种静态函数,如
template <size_t I, typename T1, typename T2> struct Helpers
{
static void add(ShortVector& dst, const ShortVector<T1, N>& lhs, const ShortVector<T2, N>& rhs)
{
dst.get<I>() = lhs.get<I>() + rhs.get<I>();
Helpers<I - 1, T1, T2>::add(dst, lhs, rhs);
}
...
};
template <typename T1, typename T2> struct Helpers < 0, T1, T2 >
{
static void add(ShortVector& dst, const ShortVector<T1, N>& lhs, const ShortVector<T2, N>& rhs)
{
dst.get<0>() = lhs.get<0>() + rhs.get<0>();
}
...
};
为所有操作员编写这样的静态函数和特殊化只是感觉不对。以这种方式编写更复杂的操作非常容易出错。我正在寻找的是像
static_for< /*Whatever's needed to define something like a run-time for cycle*/, template <size_t I, typename... Args> class Functor>();
或者几乎任何让我省略大部分样板代码的东西。我已经开始编写这样一个类,但我无法通过合理的专业化来编译它。我觉得我仍然缺乏编写这样一个类(或函数)的技能。我查看了其他库,例如Boost MPL,但还没有完全致力于使用它。我还查看了std :: index_sequence,它也可能有用。
虽然std :: index_sequence似乎是最便携的解决方案,但它有一个我不愿意看的重大缺陷。最终这些类必须SYCL兼容,这意味着我只能使用C ++ 11,包括模板元编程技术。 std :: integer_sequence是一个C ++ 14 STL库的补充,虽然这种语言标准的限制只对语言特性有影响,但没有什么能阻止STL实现者在实现C ++ 14 STL时使用C ++ 14语言特性功能,因此使用C ++ 14 STL功能可能无法移植。
我愿意接受建议甚至是解决方案。
修改
Here是我到目前为止所得到的。这是我开始收集的模板元编程技巧的标题,而for循环将是下一个。帮助器需要一个函子,它具有运行索引作为它的第一个参数,并接受各种谓词。只要下一次迭代的谓词成立,它就会继续实例化仿函数。运行索引可以增加任何数字,乘以数字等等。
答案 0 :(得分:5)
您可以查看Boost Fusion的算法。
所需要的只是将您的类型调整为融合序列。
简单示例: Live On Coliru
#include <boost/array.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
int main()
{
using namespace boost;
boost::array<int, 4> iv4 { 1,2,3,4 };
boost::array<double, 4> id4 { .1, .2, .3, .4 };
auto r = fusion::transform(iv4, id4, [](auto a, auto b) { return a+b; });
std::cout << r;
}
打印:
(1.1 2.2 3.3 4.4)
答案 1 :(得分:2)
这个怎么样:
template <size_t I, typename Functor, typename = std::make_index_sequence<I>>
struct Apply;
template <size_t I, typename Functor, std::size_t... Indices>
struct Apply<I, Functor, std::index_sequence<Indices...>> :
private std::tuple<Functor> // For EBO with functors
{
Apply(Functor f) : std::tuple<Functor>(f) {}
Apply() = default;
template <typename InputRange1, typename InputRange2, typename OutputRange>
void operator()(OutputRange& dst,
const InputRange1& lhs, const InputRange2& rhs) const
{
(void)std::initializer_list<int>
{ (dst.get<Indices>() = std::get<0>(*this)(lhs.get<Indices>(),
rhs.get<Indices>()), 0)... };
}
};
用法可能是
Apply<4,std::plus<>>()(dest, lhs, rhs); // Size or functor type
// can be deduced if desired
A(稍加修改)示例:Demo。
如果它以任何方式阻碍你,你也可以删除仿函数状态:
template <size_t I, typename Functor, typename = std::make_index_sequence<I>>
struct Apply;
template <size_t I, typename Functor, std::size_t... Indices>
struct Apply<I, Functor, std::index_sequence<Indices...>>
{
template <typename InputRange1, typename InputRange2, typename OutputRange>
void operator()(OutputRange& dst,
const InputRange1& lhs, const InputRange2& rhs) const
{
(void)std::initializer_list<int>
{ (dst.get<Indices>() = Functor()(lhs.get<Indices>(),
rhs.get<Indices>()), 0)... };
}
};
答案 2 :(得分:1)
关于
“我仅限于使用C ++ 11,包括模板元编程技术。 std::integer_sequence
是一个C ++ 14 STL库添加[...]
...你可以用例如g ++编译器:
namespace my {
using std::tuple;
using std::tuple_cat;
template< int i >
struct Number_as_type_ {};
template< int... values >
using Int_sequence_ = tuple< Number_as_type_<values>... >;
template< class Int_seq_a, class Int_seq_b >
using Concat_ = decltype( tuple_cat( Int_seq_a(), Int_seq_b() ) );
template< int max_index >
struct Index_sequence_t_
{
using T = Concat_<
typename Index_sequence_t_<max_index-1>::T, Int_sequence_<max_index>
>;
};
template<>
struct Index_sequence_t_<0> { using T = Int_sequence_<0>; };
template< int n_indices >
using Index_sequence_ = typename Index_sequence_t_<n_indices - 1>::T;
} // namespace my
不幸的是,Visual C ++ 12.0(2013)对上述Int_sequence_
的模板参数推断进行了扼杀。显然,它与错误地将模板化的using
视为类中自动引用的本地typedef
有关。无论如何,使用对Visual C++ compiler bug的理解,我重写了上面的内容,如下所示,它似乎也适用于Visual C ++:
namespace my {
using std::tuple;
using std::tuple_cat;
template< int i >
struct Number_as_type_ {};
template< int... values >
struct Int_sequence_
{
using As_tuple = tuple< Number_as_type_<values>... >;
};
template< int... values >
auto int_seq_from( tuple< Number_as_type_<values>... > )
-> Int_sequence_< values... >;
template< class Int_seq_a, class Int_seq_b >
using Concat_ = decltype(
int_seq_from( tuple_cat(
typename Int_seq_a::As_tuple(), typename Int_seq_b::As_tuple()
) )
);
template< int n_indices >
struct Index_sequence_t_
{
using T = Concat_<
typename Index_sequence_t_<n_indices-1>::T, Int_sequence_<n_indices-1>
>;
};
template<>
struct Index_sequence_t_<1> { using T = Int_sequence_<0>; };
template< int n_indices >
using Index_sequence_ = typename Index_sequence_t_<n_indices>::T;
} // namespace my
通过上面提供的基于C ++ 11的支持,一般的编译时索引for
循环,或者,如果你愿意的话,可以实现基于模板的循环展开C ++ 11,这样可以写出这样的代码:
template< int i >
struct Add_
{
void operator()( int sum[], int const a[], int const b[] ) const
{
sum[i] = a[i] + b[i];
}
};
#include <iostream>
using namespace std;
auto main() -> int
{
int sum[5];
int const a[] = {1, 2, 3, 4, 5};
int const b[] = {100, 200, 300, 400, 500};
my::for_each_index<5, Add_>( sum, a, b );
for( int x: sum ) { cout << x << ' '; } cout << endl;
}
请注意,虽然这可能出现是切片披萨以来的最佳选择,但我怀疑任何相当不错的编译器都会循环展开优化,当然,即引入这种额外的复杂性并不一定有任何好处。
与优化一样,请执行 MEASURE 。
这个设计完全循环展开,也就是说,不是 n 执行具有不同索引的循环体,而是获得具有不同索引值的循环体的 n 实例。它不一定是最好的方法,例如因为较大的代码在缓存中适应的可能性较小(重复:对于优化总是 measure ),并且对于并行性,您可能有特殊要求。您可以查看“Duff的设备”,了解更有限的循环展开技术。
namespace my {
using std::forward;
using std::initializer_list;
template< class Type >
void evaluate( initializer_list< Type > const& ) {}
namespace impl {
template< template <int> class Functor_, class... Args >
struct Call_with_numbers_
{
template< int... numbers >
void operator()( Int_sequence_<numbers...> const&, Args&&... args ) const
{
evaluate( {(Functor_<numbers>()( args... ), 0)...} );
}
};
} // namespace impl
template< int n, template<int> class Functor_, class... Args >
void for_each_index( Args&&... args )
{
using Seq = Index_sequence_<n>;
Seq s;
impl::Call_with_numbers_< Functor_, Args... >()( s, forward<Args>( args )... );
}
} // namespace my
免责声明:深夜编码,所以不一定非常完美! : - /