2012-10-23 35 views
6

एक इनपुट स्ट्रीम पर पुनरावृति करने के लिए, हम आम तौर पर तो जैसे एक std::istream_iterator का प्रयोग करेंगे:रेंज आधारित एक इनपुट स्ट्रीम पर पाश

typedef std::istream_iterator<std::string> input_iterator; 

std::ifstream file("myfile"); 
for (input_iterator i(file); i != input_iterator(); i++) { 
    // Here, *i denotes each element extracted from the file 
} 

यह अच्छा होगा यदि हम करने के लिए सीमा के आधार पर for बयान का इस्तेमाल कर सकते हो जाएगा इनपुट धाराओं पर फिर से शुरू करें।

  • if _RangeT is an array type, begin-expr and end-expr are __range and __range + __bound , respectively, where __bound is the array bound. If _RangeT is an array of unknown size or an array of incomplete type, the program is ill-formed;

  • if _RangeT is a class type, the unqualified-idsbegin and end are looked up in the scope of class _RangeT as if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, begin-expr and end-expr are __range.begin() and __range.end() , respectively;

  • otherwise, begin-expr and end-expr are begin(__range) and end(__range) , respectively, where begin and end are looked up with argument-dependent lookup (3.4.2). For the purposes of this name lookup, namespace std is an associated namespace.

इनपुट धाराओं इन सदस्य कार्यों की जरूरत नहीं है: हालांकि, वर्ग प्रकार की वस्तुओं के लिए, सीमा आधारित for वस्तु begin() और end() सदस्य कार्य (§6.5.4, बोल्ड जोर जोड़ा) करने की आवश्यकता है (वे कंटेनर नहीं हैं) और इसलिए श्रेणी-आधारित for उन पर काम नहीं करेगा। यह वैसे भी समझ में आता है क्योंकि उपरोक्त मामले में निकालने के लिए आपको निर्दिष्ट करने के लिए कुछ तरीका चाहिए (std::string)।

लेकिन अगर हम जानते हैं कि हम निकालना चाहते हैं, यह संभव हमारे अपने begin() और end() कार्यों (शायद विशेषज्ञताओं या std::begin() और std::end() के भार के) इनपुट ऐसी धाराओं के लिए है कि वे के रूप में वर्ग के सदस्य पहुंच देखने के द्वारा पाया जा होगा परिभाषित करने के लिए है ऊपर वर्णित?

यह §6.5.4 से अस्पष्ट (कम से कम मेरे लिए) अस्पष्ट है यदि पिछले लुकअप विफल होने पर फ़ंक्शन को तर्क-निर्भर लुकअप के साथ देखा जाएगा या नहीं। विचार करने की एक और बात यह है कि std::ios_base और इसके डेरिवेटिव्स के पास पहले से ही end नामक सदस्य है जो मांगने के लिए एक ध्वज है।

std::ifstream file("myfile"); 
for (const std::string& str : file) { 
    // Here, str denotes each element extracted from the file 
} 

या::

std::ifstream file("myfile"); 
for (auto i = begin(file); i != end(file); i++) { 
    // Here, *i denotes each element extracted from the file 
} 
+0

क्या यह सिर्फ मुझे है, या यह कल्पना से बहुत अस्पष्ट है? ऐसा लगता है कि 'std :: start()' और 'std :: end()' केवल तभी मिलेगा जब '_RangeT' सरणी या वर्ग प्रकार का नहीं है। –

+0

हाँ, यह सबसे अच्छा शब्द नहीं है, लेकिन मुझे लगता है कि आप इसे पढ़ने के इरादे से हैं "यदि यह एक वर्ग है और इसमें है .begin और .end तो यह उन लोगों का उपयोग करेगा ... अन्यथा, यानी आप मुफ्त कार्यों को प्रदान कर सकते हैं । –

+1

"*' start' और 'end' को कक्षा _ रेंजटी के दायरे में देखा जाता है ... और यदि ** या तो ... कम से कम एक घोषणा **, 'प्रारंभ-expr' और' end-expr' पाता है) '__range.begin()' और '__range.end()' * "- चूंकि' std :: ios_base :: end' मौजूद है (और इस प्रकार 'std :: ifstream :: end' मिलेगा) गेम ऊपर है । '.begin()' नहीं मिलेगा, और '.end()' एक वाक्यविन्यास त्रुटि होगी। –

उत्तर

1

इससे कोई फर्क नहीं है कि वे तर्क पर निर्भर देखने से मिल जाएगा या नहीं, क्योंकि आप कक्षाओं की विशेषज्ञताओं डाल करने के लिए अनुमति दी जाती है

यहाँ इच्छित परिणाम है और std नामस्थान में कार्य करता है।

+0

लेकिन, जहां तक ​​मुझे पता है, आपको अधिभार जोड़ने की अनुमति नहीं है, और चूंकि आप आंशिक रूप से कार्यों को विशेषज्ञ नहीं कर सकते हैं, इसलिए आपको या तो सभी 'std :: istream_iterator ' के लिए पूर्ण विशेषज्ञता जोड़ने की आवश्यकता होगी या केवल इसके लिए विशेषज्ञता जोड़ें आपको जिसकी जरूरत है –

+0

@ पीटर एलेक्सेंडर ओह, मुझे यकीन नहीं है कि मैं समझता हूं। "सभी के लिए पूर्ण विशेषज्ञता" isdream_iterator ' –

+0

@ पीटर एलेक्सेंडर निश्चित रूप से हमें 'std :: basic_istream' के लिए विशेषज्ञता की आवश्यकता होगी? –

1

यहां एक संभावित समाधान है। दुख की बात है, यह एक अतिरिक्त संरचना की आवश्यकता है:

#include <iostream> 
#include <fstream> 
#include <iterator> 
#include <algorithm> 
#include <string> 

struct S { 
    std::istream& is; 
    typedef std::istream_iterator<std::string> It; 
    S(std::istream& is) : is(is) {} 
    It begin() { return It(is); } 
    It end() { return It(); } 
}; 

int main() { 
    std::ifstream file("myfile"); 
    for(auto& string : S(file)) { 
    std::cout << string << "\n"; 
    } 
} 

एक अन्य समाधान std::ifstream से निकाले जाते हैं के लिए है:

#include <iostream> 
#include <fstream> 
#include <iterator> 
#include <algorithm> 
#include <string> 


struct ifstream : std::ifstream { 
    // using std::ifstream::ifstream; I wish g++4.7 supported inheriting constructors! 
    ifstream(const char* fn) : std::ifstream(fn) {} 
    typedef std::istream_iterator<std::string> It; 
    It begin() { return It(*this); } 
    It end() { return It(); } 
}; 

int main() { 
    ifstream file("myfile"); 
    for(auto& string : file) { 
    std::cout << string << "\n"; 
    } 
} 
4

एक स्पष्ट दृष्टिकोण के प्रकार और आवश्यक इंटरफेस प्रदान अपनी स्ट्रीम के लिए एक सरल डेकोरेटर का उपयोग है ।

template <typename T> 
struct irange 
{ 
    irange(std::istream& in): d_in(in) {} 
    std::istream& d_in; 
}; 
template <typename T> 
std::istream_iterator<T> begin(irange<T> r) { 
    return std::istream_iterator<T>(r.d_in); 
} 
template <typename T> 
std::istream_iterator<T> end(irange<T>) { 
    return std::istream_iterator<T>(); 
} 

for (auto const& x: irange<std::string>(std::ifstream("file") >> std::skipws)) { 
    ... 
} 
+0

ले सकता है कोई एक अप्रयुक्त पैरामीटर चेतावनी से बचने के लिए' std :: istream_iterator अंत (इरेंज r) में परिवर्तनीय नाम 'r' को हटा सकता है। – Mankka

+0

@Mankka: किया गया। मुद्दे को इंगित करने के लिए धन्यवाद। –

0

मैं std::begin और std::endstd::basic_istream से प्राप्त वर्गों के लिए विशेषज्ञता के विचार को आगे बढ़ाने का प्रयास किया (मैं इस टेम्पलेट metaprogramming व्यापार पर इतना महान नहीं कर रहा हूँ):

namespace std 
{ 
    template <typename C> 
    typename 
    std::enable_if< 
    std::is_base_of<std::basic_istream<typename C::char_type>, C>::value, 
    std::istream_iterator<std::string>>::type 
    begin(C& c) 
    { 
    return {c}; 
    } 

    template <typename C> 
    typename 
    std::enable_if< 
    std::is_base_of<std::basic_istream<typename C::char_type>, C>::value, 
    std::istream_iterator<std::string>>::type 
    end(C& c) 
    { 
    return {}; 
    } 
} 
यहाँ की तरह इस कैसा नज़र आ सकता है

दरअसल, यह बहुत अच्छी तरह से काम करता है। मैंने ऐसे संस्करण नहीं बनाए जो const C& लेते हैं क्योंकि मुझे नहीं लगता कि यह एक कॉन्स्ट स्ट्रीम से निकालने के लिए समझ में आता है (और जब मैंने ऐसा करने की कोशिश की तो मुझे त्रुटियां थीं)। मुझे यह भी यकीन नहीं है कि क्या मैं इसे और अधिक अनुकूल बना सकता हूं।तो अब मैं बहुत तरह myfile की सामग्री को प्रिंट कर सकते हैं ::

std::ifstream file("myfile"); 
std::copy(begin(file), end(file), std::ostream_iterator<std::string>(std::cout, " ")); 

तो इन begin और end कार्यों अपेक्षित तरीके से। हालांकि, यह श्रेणी-आधारित for लूप में उपयोग किए जाने पर फ्लैट गिरता है। std::basic_istream कक्षाएं std::ios_base से ली गई हैं जिनके पास पहले से ही end नामक सदस्य है (यह स्ट्रीम के भीतर खोज के लिए ध्वज है)। एक बार जब सीमा आधारित for पाश इस पाता है, यह सिर्फ देता है, क्योंकि यह नहीं मिल सकता है एक इसी begin (कि end संस्था की सही तरह नहीं है का उल्लेख नहीं करने के लिए):

main.cpp:35:33: error: range-based ‘for’ expression of type ‘std::basic_ifstream’ has an ‘end’ member but not a ‘begin’

एकमात्र विकल्प है कि काम करता है दोनों स्थितियों में, जैसा कि अन्य ने उल्लेख किया है, एक रैपर ऑब्जेक्ट बनाना है। दुर्भाग्यवश endstd::ios_base में सदस्य पूरी तरह से इसे एक अच्छे तरीके से कार्यान्वित करने का कोई मौका बर्बाद कर देता है।