Clean ways to write multiple 'for' loops

前端 未结 16 949
傲寒
傲寒 2020-12-22 15:22

For an array with multiple dimensions, we usually need to write a for loop for each of its dimensions. For example:

vector< vector< vector         


        
16条回答
  •  庸人自扰
    2020-12-22 15:52

    Something like this can help:

     template 
     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 > > 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; });
     }
    

    In order to make it N-ary we need some template magic. First of all we should create SFINAE structure to distinguish whether this value or container. The default implementation for values, and specialisations for arrays and each of the container types. How @Zeta notes, we can determine the standard containers by the nested iterator type (ideally we should check whether the type can be used with range-base for or not).

     template 
     struct has_iterator
     {
         template 
         constexpr static std::true_type test(typename C::iterator *);
    
         template 
         constexpr static std::false_type test(...);
    
         constexpr static bool value = std::is_same<
             std::true_type, decltype(test::type>(0))
         >::value;
     };
    
     template 
     struct is_container : has_iterator {};
    
     template 
     struct is_container : std::true_type {};
    
     template 
     struct is_container : std::true_type {}; 
    
     template 
     struct is_container> : std::true_type {};
    

    Implementation of for_each is straightforward. The default function will call function:

     template 
     typename std::enable_if::value, void>::type
     rfor_each(const Value &value, Function function)
     {
         function(value);
     }
    

    And the specialisation will call itself recursively:

     template 
     typename std::enable_if::value, void>::type
     rfor_each(const Container &container, Function function)
     {
         for (const auto &i: container)
             rfor_each(i, function);
     }
    

    And voila:

     int main()
     {
         using namespace std;
         vector< vector< vector > > A;
         A.resize(3, vector >(3, vector(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,
     }
    

    Also this will not work for pointers (arrays allocated in heap).

提交回复
热议问题