对于具有多个维度的数组,我们通常需要for
为每个维度编写一个循环.例如:
vector< vector< vector<int> > > A;
for (int k=0; k<A.size(); k++)
{
for (int i=0; i<A[k].size(); i++)
{
for (int j=0; j<A[k][i].size(); j++)
{
do_something_on_A(A[k][i][j]);
}
}
}
double B[10][8][5];
for (int k=0; k<10; k++)
{
for (int i=0; i<8; i++)
{
for (int j=0; j<5; j++)
{
do_something_on_B(B[k][i][j]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你for-for-for
经常在我们的代码中看到这种循环.如何使用宏来定义for-for-for
循环,这样我每次都不需要重写这种代码?有一个更好的方法吗?
Jam*_*nze 280
首先,你不要使用这样的数据结构.如果需要三维矩阵,可以定义一个:
class Matrix3D
{
int x;
int y;
int z;
std::vector<int> myData;
public:
// ...
int& operator()( int i, int j, int k )
{
return myData[ ((i * y) + j) * z + k ];
}
};
Run Code Online (Sandbox Code Playgroud)
或者,如果要使用索引[][][]
,则需要使用operator[]
返回代理的方法.
一旦你完成了这个,如果你发现你不得不像你所呈现的那样进行迭代,你会暴露一个支持它的迭代器:
class Matrix3D
{
// as above...
typedef std::vector<int>::iterator iterator;
iterator begin() { return myData.begin(); }
iterator end() { return myData.end(); }
};
Run Code Online (Sandbox Code Playgroud)
那你就写:
for ( Matrix3D::iterator iter = m.begin(); iter != m.end(); ++ iter ) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
(要不就:
for ( auto& elem: m ) {
}
Run Code Online (Sandbox Code Playgroud)
如果你有C++ 11.)
如果在这样的迭代期间需要三个索引,则可以创建一个暴露它们的迭代器:
class Matrix3D
{
// ...
class iterator : private std::vector<int>::iterator
{
Matrix3D const* owner;
public:
iterator( Matrix3D const* owner,
std::vector<int>::iterator iter )
: std::vector<int>::iterator( iter )
, owner( owner )
{
}
using std::vector<int>::iterator::operator++;
// and so on for all of the iterator operations...
int i() const
{
((*this) - owner->myData.begin()) / (owner->y * owner->z);
}
// ...
};
};
Run Code Online (Sandbox Code Playgroud)
Sho*_*hoe 43
使用宏来隐藏for
循环可能会让人感到困惑,只是为了节省很少的字符.我会改用range-for循环:
for (auto& k : A)
for (auto& i : k)
for (auto& j : i)
do_something_on_A(j);
Run Code Online (Sandbox Code Playgroud)
当然auto&
,const auto&
如果您实际上没有修改数据,则可以替换.
fas*_*ked 21
这样的东西可以帮助:
template <typename Container, typename Function>
void for_each3d(const Container &container, Function function)
{
for (const auto &i: container)
for (const auto &j: i)
for (const auto &k: j)
function(k);
}
int main()
{
vector< vector< vector<int> > > A;
for_each3d(A, [](int i){ std::cout << i << std::endl; });
double B[10][8][5] = { /* ... */ };
for_each3d(B, [](double i){ std::cout << i << std::endl; });
}
Run Code Online (Sandbox Code Playgroud)
为了使它成为N-ary,我们需要一些模板魔法.首先我们应该创建SFINAE结构来区分这个值还是容器.值的默认实现,以及数组和每个容器类型的特化.@Zeta如何注意,我们可以通过嵌套iterator
类型确定标准容器(理想情况下,我们应该检查类型是否可以与范围库一起使用for
).
template <typename T>
struct has_iterator
{
template <typename C>
constexpr static std::true_type test(typename C::iterator *);
template <typename>
constexpr static std::false_type test(...);
constexpr static bool value = std::is_same<
std::true_type, decltype(test<typename std::remove_reference<T>::type>(0))
>::value;
};
template <typename T>
struct is_container : has_iterator<T> {};
template <typename T>
struct is_container<T[]> : std::true_type {};
template <typename T, std::size_t N>
struct is_container<T[N]> : std::true_type {};
template <class... Args>
struct is_container<std::vector<Args...>> : std::true_type {};
Run Code Online (Sandbox Code Playgroud)
实施for_each
很简单.默认函数将调用function
:
template <typename Value, typename Function>
typename std::enable_if<!is_container<Value>::value, void>::type
rfor_each(const Value &value, Function function)
{
function(value);
}
Run Code Online (Sandbox Code Playgroud)
专业化将递归地调用自己:
template <typename Container, typename Function>
typename std::enable_if<is_container<Container>::value, void>::type
rfor_each(const Container &container, Function function)
{
for (const auto &i: container)
rfor_each(i, function);
}
Run Code Online (Sandbox Code Playgroud)
瞧:
int main()
{
using namespace std;
vector< vector< vector<int> > > A;
A.resize(3, vector<vector<int> >(3, vector<int>(3, 5)));
rfor_each(A, [](int i){ std::cout << i << ", "; });
// 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
std::cout << std::endl;
double B[3][3] = { { 1. } };
rfor_each(B, [](double i){ std::cout << i << ", "; });
// 1, 0, 0, 0, 0, 0, 0, 0, 0,
}
Run Code Online (Sandbox Code Playgroud)
这也不适用于指针(在堆中分配的数组).
kur*_*eko 17
大多数答案只是演示了如何将C++扭曲成难以理解的语法扩展,恕我直言.
通过定义任何模板或宏,您只需强制其他程序员理解一些混淆代码,这些代码旨在隐藏其他混淆代码.
您将强制每个读取代码的人都具有模板专业知识,以避免您使用明确的语义来定义对象.
如果您决定使用三维数组等原始数据,只需使用它,或者定义一个为您的数据提供一些可理解含义的类.
for (auto& k : A)
for (auto& i : k)
for (auto& current_A : i)
do_something_on_A(current_A);
Run Code Online (Sandbox Code Playgroud)
与int的向量向量的神秘定义一致,没有明确的语义.
Fre*_*ame 10
#include "stdio.h"
#define FOR(i, from, to) for(int i = from; i < to; ++i)
#define TRIPLE_FOR(i, j, k, i_from, i_to, j_from, j_to, k_from, k_to) FOR(i, i_from, i_to) FOR(j, j_from, j_to) FOR(k, k_from, k_to)
int main()
{
TRIPLE_FOR(i, j, k, 0, 3, 0, 4, 0, 2)
{
printf("i: %d, j: %d, k: %d\n", i, j, k);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
更新:我知道,你要求它,但你最好不要使用它:)
一个想法是编写一个可迭代的伪容器类,它"包含"您将索引的所有多索引元组的集合.这里没有实施,因为它需要太长时间,但想法是你应该能够写...
multi_index mi (10, 8, 5);
// The pseudo-container whose iterators give {0,0,0}, {0,0,1}, ...
for (auto i : mi)
{
// In here, use i[0], i[1] and i[2] to access the three index values.
}
Run Code Online (Sandbox Code Playgroud)