C ++ үшін «кірістіліктің» түйінді сөзі, функциядан қалайша Итераторды алу керек?

Келесі кодты қарастырыңыз.

 std::vector<result_data> do_processing() { pqxx::result input_data = get_data_from_database(); return process_data(input_data); } std::vector<result_data> process_data(pqxx::result const  input_data) { std::vector<result_data> ret; pqxx::result::const_iterator row; for (row = input_data.begin(); row != inpupt_data.end(); ++row) { // somehow populate output vector } return ret; } 

Мен қайтару құндылығын оңтайландыру (RVO) орын алуы мүмкін деп ойлаған кезде, осы жауапты Джерри Коффин [менің акцентім] деп таптым:

Кем дегенде IMO, бұл әдетте жаман идея, бірақ тиімділікке байланысты емес. Бұл жаман идея, себебі бұл функция, әдетте , оның иератор арқылы нәтижесін беретін жалпы алгоритм ретінде жазылуы керек . Итераторлармен жұмыс істеудің орнына контейнерді қабылдайтын немесе қайтаратын дерлік код күдікті деп санауға тиіс.

Мені дұрыс түсінбеңіз: бір-біріне ұқсайтын объектілерді (мысалы, сызықтарды) тасымалдаудың мағынасы бар, бірақ жоғарыда келтірілген мысалда векторды жаман идеяға өтуді немесе қайтаруды қарастырамын.

Кейбір фондық Python бар болса, мен генераторларды ұнатамын. Шын мәнінде, егер бұл Python болса, мен жоғарыда аталған функцияны генератор ретінде жазатынмын, яғни, Басқа да нәрселерден бұрын барлық деректерді өңдеу қажеттілігін болдырмау. Мысалы, мысалы:

10 авг. set moooeeeep 10 тамыз 2012-08-10 12:17 '12 at 12:17 2012-08-10 12:17
@ 4 жауаптар

Жоқ, бұл Jerry дегенді білдірмейді, кем дегенде тікелей емес.

Python-дағы yield корутинді іске асырады. C ++-де оларды жоқ (бірақ, әрине, олар эмуляциялануы мүмкін, бірақ бұл таза түрде жасалынған болса, аздап байланысты).

Бірақ, Джерридің сөзі - бұл шығыс итераторынан өту керек, ол келесіге жазылады:

 template <typename O> void process_data(pqxx::result const  input_data, O iter) { for (row = input_data.begin(); row != inpupt_data.end(); ++row) *iter++ = some_value; } 

Және оны атаңыз:

 std::vector<result_data> result; process_data(input, std::back_inserter(result)); 

Бұл векторды қайтарудан гөрі әдеттегідей жақсы екеніне көз жеткізе бермеймін.

16
10 авг. жауап Конрад Рудольф 10 тамыз 2012-08-10 12:26 '12 сағат 12:26 pm 2012-08-10 12:26

Бұл туралы автор Boost.Asio Chris Kohlhoff блогының блогы бар: http://blog.think-async.com/2009/08/secret-sauce-revealed.html

Бұл макростардың үлгілері.

border=0
 #define yield \ if ((_coro_value = __LINE__) == 0) \ { \ case __LINE__: ; \ (void) \ } \ else \ for (bool _coro_bool = false;; \ _coro_bool = !_coro_bool) \ if (_coro_bool) \ goto bail_out_of_coroutine; \ else 

Бұл coroutine класспен бірге қолданылуы керек. Мәліметтер алу үшін блогты қараңыз.

12
10 авг. Бұл сұрақтың жауабы TemplateRex 10 тамызда берілген. 2012-08-10 12:24 '12 сағат 12:24 pm 2012-08-10 12:24

Өзгерістерді талдау кезінде немесе өңдеуші мемлекеттің күйі болған кезде, генератордың үлгісі жақсы идея болып табылады және кодты айтарлықтай жеңілдетеді, сондықтан оны қайталау мүмкін емес, әдетте кері байланыс балама болып табылады. Мен yield ие болғым келеді және Boost.Coroutine2 енді қолдануға болатын сияқты.

Төмендегі код cat файлдарға мысал. Әрине, мәтіндік жолдарды өңдегіңіз келмейінше, бұл мәнсіз:

 #include <fstream> #include <functional> #include <iostream> #include <string> #include <boost/coroutine2/all.hpp> using namespace std; typedef boost::coroutines2::coroutine<const string coro_t; void cat(coro_t::push_type yield, int argc, char* argv[]) { for (int i = 1; i < argc; ++i) { ifstream ifs(argv[i]); for (;;) { string line; if (getline(ifs, line)) { yield(line); } else { break; } } } } int main(int argc, char* argv[]) { using namespace std::placeholders; coro_t::pull_type seq( boost::coroutines2::fixedsize_stack(), bind(cat, _1, argc, argv)); for (auto line : seq) { cout << line << endl; } } 
3
06 авг. Жауапты Yongwei Wu 06 тамыз күні берілді . 2016-08-06 12:12 '16 at 12:12 2016-08-06 12:12

Мен бұл мінез-құлықты өзім ойлағандай ұқсас деп таптым. Келесі (расталмаған) кодты қарастырыңыз:

 struct data_source { public: // for delivering data items data_source operator>>(input_data_t  i) { i = input_data.front(); input_data.pop_front(); return *this; } // for boolean evaluation operator void*() { return input_data.empty() ? 0 : this; } private: std::deque<input_data_t> input_data; // appends new data to private input_data // potentially asynchronously void get_data_from_database(); }; 

Енді келесі мысалда көрсетілгендей жасай аламын:

 int main () { data_source d; input_data_t i; while (d >> i) { // somehow process items result_data_t r(i); cout << r << endl; } } 

Осылайша, деректер жинау қандай да бір түрде өңдеуден бөлінеді және осылайша ленивый / асинхронды болуы мүмкін. Яғни элементтерді өңдей аламын және оларды басқа мысалдағы сияқты вектор толы болғанша күтудің қажеті жоқ.

0
15 сент. Жауап 15 маусымда беріледі 2012-09-15 13:54 '12 at 13:54 2012-09-15 13:54

тегтеріне қатысты басқа сұрақтар, немесе сұрақ қойыңыз