2012-07-01 25 views
10

tôi có mã như thế nàyTại sao không phạm vi cho tìm thấy quá tải của tôi bắt đầu và kết thúc cho std :: istream_iterator?

std::ifstream file(filename, std::ios_base::in); 
if(file.good()) 
{ 
    file.imbue(std::locale(std::locale(), new delimeter_tokens())); 
    for(auto& entry : std::istream_iterator<std::string>(file)) 
    { 
     std::cout << entry << std::endl;  
    } 
} 
file.close(); 

nơi std::istream_iterator<std::string> 's begin()end() được định nghĩa như sau

template<class T> 
std::istream_iterator<T> begin(std::istream_iterator<T>& stream) 
{ 
    return stream; 
} 

template<class T> 
std::istream_iterator<T> end(std::istream_iterator<T>& stream) 
{ 
    return std::istream_iterator<T>(); 
} 

đó là những gì Mark Nelson cũng đã viết về trong Dr. Dobb của here. Than ôi, mã thất bại trong việc biên dịch trên Visual Studio của tôi năm 2012 với thông báo lỗi

lỗi C3312: không có chức năng callable 'bắt đầu' tìm thấy cho loại 'std :: istream_iterator < _Ty>'

lỗi C3312: không có chức năng callable 'kết thúc' tìm thấy cho loại 'std :: istream_iterator < _Ty>'

Câu hỏi: Có điều gì đó tôi không nhận thấy, lỗi trong trình biên dịch (không, nhưng chỉ trong trường hợp) hoặc ... Vâng, có ý tưởng nào không?


Câu hỏi này được làm sạch đáng kể, theo lời khuyên của Xeo. Để cung cấp nhiều nền và tham chiếu hơn, điều này có liên quan đến số other question trên Stackoverflow của tôi, tôi đã tự hỏi làm thế nào để làm cho việc phân tích cú pháp dựa trên dòng sạch hơn các vòng thông thường. Một chút mã hóa và kiểm tra từ internet, và tôi đã có một bản phác thảo làm việc như sau

std::ifstream file(filename, std::ios_base::in); 
if(file.good()) 
{    
    file.imbue(std::locale(std::locale(), new delimeter_tokens())); 
    for(auto& entry : istream_range<std::string>(file) 
    { 
     std::cout << entry << std::endl;  
    } 
} 
file.close(); 

nhưng có một chút snag tôi đã cố gắng khắc phục. Tôi nghĩ rằng nó sẽ trông tự nhiên hơn để viết như trong mã mà không biên dịch và không giống như

for(auto& entry : istream_range<std::string>(file) 

Xin vui lòng, lưu ý của trình lặp khác nhau. delimeter_tokens được định nghĩa là Nawaz vui lòng hiển thị here (mã không trùng lặp) và istream_range như trong blog Tổng hợp mã here. Tôi nghĩ việc triển khai bắt đầu và kết thúc sẽ hoạt động, như được quảng cáo trong bài viết trên blog Code Synthesis nói trên

Quy tắc cuối cùng (dự phòng cho các hàm start-free() và end() miễn phí cho phép chúng ta thích ứng không xâm lấn đến phạm vi dựa trên giao diện vòng lặp.

Do đó, câu hỏi của tôi với tất cả (ir) nền có liên quan.

+0

Tôi đã tự do thay đổi tiêu đề câu hỏi để phản ánh câu hỏi thực tế được hỏi. Quay trở lại hoặc thay đổi nếu cần thiết. – Xeo

+0

Nó tốt hơn thế, tôi nghĩ vậy. Nó được sự chú ý của những người đang tìm kiếm một giải pháp tương tự như tôi và những người có vấn đề tra cứu để giải quyết. Làm thế nào để làm cho bài viết của bạn như là một câu trả lời (hoặc có ai đó hoàn thành hơn làm điều đó)? – Veksi

+0

Cảm ơn bạn vì điều đó! : D – Veksi

Trả lời

7

Phạm vi cho dựa trên ADL nếu xử lý đặc biệt cho mảng gốc (T foo[N]) và thành viên begin/end không mang lại kết quả nào.

§6.5.4 [stmt.ranged] p1

  • khác, bắt đầu-exprcuối exprbegin(__range)end(__range), tương ứng, nơi beginend đang nhìn lên với tra cứu luận phụ thuộc vào (3.4.2). Với mục đích tra cứu tên này, không gian tên std là một không gian tên được liên kết.

Vấn đề của bạn là, không gian tên liên quan của std::istream_iterator là (rõ ràng) namespace std, không phải là không gian tên toàn cầu.

§3.4.2 [basic.lookup.argdep] p2

Đối với mỗi loại lập luận T trong cuộc gọi chức năng, có một tập hợp các số không hay liên quan nhiều không gian tên và một bộ không hoặc liên quan nhiều lớp học để được xem xét. Các tập các không gian tên và các lớp được xác định hoàn toàn bởi các kiểu đối số hàm [...].

  • Nếu kiểu cơ bản, các nhóm không gian tên và lớp liên quan của nó đều trống.
  • Nếu T là loại lớp (kể cả công đoàn), các lớp liên quan của nó là: chính lớp đó; lớp mà nó là thành viên, nếu có; và các lớp cơ bản trực tiếp và gián tiếp của nó. Các không gian tên được liên kết của nó là các không gian tên trong đó các lớp liên kết của nó là các thành viên. Hơn nữa, nếu T là một chuyên môn mẫu lớp, các không gian tên và lớp liên quan của nó cũng bao gồm: các không gian tên và các lớp được liên kết với các kiểu đối số mẫu được cung cấp cho các tham số kiểu mẫu [...].

Lưu ý phần cuối cùng (được trích dẫn) của viên đạn thứ hai. Về cơ bản nó có nghĩa là sử dụng một lớp là thành viên của không gian tên chung làm đối số mẫu làm cho mã hoạt động:

#include <iterator> 
#include <iostream> 

template<class T> 
std::istream_iterator<T> begin(std::istream_iterator<T> is){ 
    return is; 
} 
template<class T> 
std::istream_iterator<T> end(std::istream_iterator<T>){ 
    return std::istream_iterator<T>(); 
} 

struct foo{}; 

std::istream& operator>>(std::istream& is, foo){ 
    return is; 
} 

int main(){ 
    for(foo f : std::istream_iterator<foo>(std::cin)) 
    //        ^^^ 
    // make global namespace one of the associated namespaces 
    ; 
} 
+0

Tôi không thể theo dõi bạn ngay bây giờ. Làm thế nào mà không gian tên hiệp hội lừa sẽ làm việc nếu tôi nên đọc chuỗi mã hóa từ luồng tập tin? Đó là, foo bạn chỉ ra trong vòng lặp nên là chuỗi trong trường hợp của tôi. – Veksi

+0

@Veksi: Về cơ bản, chỉ phần đầu tiên của câu trả lời của tôi là quan trọng: Bạn không thể đặt quá tải cho các thành viên 'std' trong không gian tên chung cho phạm vi. Phần thứ hai chỉ để giới thiệu một sự kỳ quặc của ADL cho phép làm việc. : P – Xeo

+1

Trong khi nhận xét về [câu hỏi này] (http://stackoverflow.com/a/18321288/819272), tôi đã tìm hiểu về [* vấn đề cốt lõi * 1442] (http://wg21.cmeerw.net/cwg/ issue1442) thay đổi các quy tắc một chút. Không gian tên 'std' không còn là không gian tên được liên kết và [* nó được đề cập rõ ràng rằng *] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3691.pdf)" bắt đầu và kết thúc được tra cứu trong các không gian tên được liên kết (3.4.2). [Lưu ý: tra cứu không bình thường (3.4.1) không được thực hiện. - lưu ý cuối cùng] " – TemplateRex

1

Vì đối số phụ thuộc tra cứu, trình biên dịch sẽ tìm kiếm begin()end() trong không gian tên std. Nếu bạn đặt các hàm của mình ở đó, thì mã sẽ biên dịch.

Vì tra cứu tên là một vấn đề phức tạp trong C++, tôi không hoàn toàn chắc chắn rằng trình biên dịch có hoạt động chính xác hay không.

+1

Bạn không được phép đặt quá tải vào 'không gian tên std'. – Xeo

+0

Nhưng bạn có thể chuyên mẫu, không? – jrok

+2

@jrok: Có, nhưng chỉ dành cho các loại do người dùng xác định của bạn, không phải cho các loại bắt nguồn từ 'std' (IIRC). Ngoài ra, bạn không thể * một phần * chuyên các mẫu chức năng và chức năng chuyên môn về khuôn mẫu nói chung được cau mày. – Xeo

Các vấn đề liên quan