2011-10-23 27 views
13

OK, tôi sẽ chỉ đăng chương trình hoàn chỉnh mặc dù nó có nội dung không liên quan và mã được đề cập là mã chết & hellip;Tại sao không cần trình độ chuyên môn?

#include <iostream> 
#include <fstream> 

namespace detail { 
    // Solution by Johannes Schaub alias litb 
    // http://groups.google.com/group/comp.std.c++/browse_thread/thread/b567617bfccabcad 
    template<int> struct D {}; 
    typedef char yes[1]; 
    typedef char no[2]; 

    template< class T, class U > 
    yes& f(int, D< sizeof T(*(U*)0) >* = 0); 

    template< class T, class U > 
    no& f(...); 

    template< class To, class From > 
    struct IsExplicitlyConvertible 
    { 
     enum{ yes = (sizeof detail::f< To, From >(0) == sizeof(detail::yes)) }; 
    }; 

    bool const streamsSupportWindows = 
     IsExplicitlyConvertible< std::ofstream, wchar_t const* >::yes; 
} 

class InFStream 
    : public std::ifstream 
{ 
    public: 
     InFStream() {} 
     explicit InFStream(
      char const* filename, 
      ios_base::openmode mode = ios_base::in | ios_base::out 
      ) 
      : std::ifstream(filename, mode) 
     {} 
}; 

int main() 
{ 
    using namespace std; 
    cout << (detail::streamsSupportWindows 
     ? "Windows-enabled" 
     : "Ach, no Windows support" 
     ) << endl; 
} 

Điều này biên dịch tốt với MSVC và g ++. Nhưng trong lớp học InFStream, tại sao tôi không cần phải đủ điều kiện ios_base? Hoặc, cùng một câu hỏi thực sự, tại sao tôi cần sử dụng tiêu chuẩn std:: của ifstream trong danh sách khởi tạo bộ dựng?

+2

Vì 'ifstream' nằm trong không gian tên std? Tại sao bạn nghĩ rằng bạn không cần phải sử dụng nó? –

+2

@VJo: 'ios_base' cũng nằm trong không gian tên' std'. Về lý do tại sao tôi nghĩ rằng các trình biên dịch tôi đề cập đến biên dịch mã này tốt, đó là bởi vì họ làm. Heh. –

+4

Có thể vì lớp InFStream của bạn kế thừa từ std :: ifstream mà lần lượt kế thừa từ std :: ios_base. Vì vậy, tất cả các liệt kê ios_base là thành viên của lớp học của bạn thông qua kế thừa và không cần đủ điều kiện. – Praetorian

Trả lời

6

Vài suy nghĩ về lý do tại sao bạn cần phải xác định std::ifstream trong initializer constructor của. Tôi nghĩ rằng typedef là thủ phạm - ifstream được định nghĩa là typedef basic_ifstream<char, char_traits<char> > ifstream;). Nếu bạn thay đổi hàm tạo của mình thành

explicit InFStream(
    char const*   filename, 
    ios_base::openmode mode = ios_base::in | ios_base::out 

    ): 
    basic_ifstream<char,std::char_traits<char>>(filename, mode){} 

bạn cũng không phải chỉ định std::basic_ifstream. Tôi không thể tìm thấy chi tiết về lý do tại sao typedef hoạt động theo cách này, nhưng sự cố có thể tái sản xuất. Ví dụ,

namespace test1 
{ 
class A { 

public : 
    static const int cn = 1; 

    virtual ~A(); 
    A(int t): x(t){}; 
    int x; 
}; 

class B:public A 
{ 
public: 
    B(int t) : A(t){}; 
}; 
typedef B XX; 
}; 
class C:public test1::XX 
{ 
    int aaa; 
    public: 
explicit C(int x) :XX(x) // error 
explicit C(int x) :test1::XX(x) // ok 
explicit C(int x) :B(x) // also ok 
{  
    aaa = A::cn; 
}; 
}; 
+0

Tôi phải chọn một chút tùy ý mà câu trả lời là "giải pháp", nhưng tôi nghĩ câu trả lời này là câu đầu tiên chỉ ra vấn đề 'typedef'. –

+0

Và để thêm, bạn chỉ có thể viết ': basic_ifstream (tên tệp, chế độ) {}'. Không cần cho danh sách đối số mẫu (và bắt đầu từ gcc4.5, gcc cuối cùng sẽ nhận được chính xác). –

0

Khi bạn lấy được từ các lớp học mà bạn có thể chỉ định

std::ifstream 

để có thể tìm thấy những lớp trong namespace std.

Trong chính mã, bạn lớp bắt nguồn từ std :: ifstream biết mọi thứ về ifstream.

Inheritance of ifstream:

ios_base -> ios -> istream -> ifstream 
+0

Cái gì của 'ios_base'? Nó cũng tồn tại trong không gian tên 'std'. –

+0

có nhưng lớp cơ sở của ifstream – Totonga

7

Sự khác biệt là ifstream không hiển thị như một tên lớp tiêm bởi vì nó là tên của một typedef, không phải là tên của class. Do đó, nó không được xem như là một tên lớp được tiêm từ lớp cơ sở.

ios_base là tên lớp chính hãng là lớp cơ sở (của lớp cơ sở) của lớp nơi nó được sử dụng và có thể nhìn thấy không đủ tiêu chuẩn làm tên lớp tiêm.

Ví dụ:

namespace X 
{ 
    class A {}; 
    template<class> class Z {}; 
    typedef Z<char> B; 
} 

class C : public X::A 
{ 
    C() : A() {} // OK, A is visible from the base class 
}; 

class D : public X::B 
{ 
    D() : B() {} // Error, B is a typedef, 
    // : X::B(), : Z<char>() or even : Z() can be used. 
}; 

Trong ví dụ của bạn, thay vì std::ifstream, bạn có thể sử dụng không đủ tiêu chuẩn basic_ifstream để thay thế. (Hoặc basic_ifstream<char> hoặc basic_ifstream<char, std::char_traits<char> > nhưng không thực sự tiết kiệm bất kỳ gõ hoặc giúp đỡ rõ ràng chút nào.)

4

quan sát khác là rằng ios_base :: openmode hoạt động, nhưng ios :: openmode không:

class InFStream 
    : public std::ifstream 
{ 
    // ... 
    ios::openmode m1;  // error: ios does not name a type 
    ios_base::openmode m2; // ok 
} 

Tôi nghĩ a1ex07 đã tìm thấy điểm mấu chốt của vấn đề: ở đây một lần nữa, ios_base là tên của một lớp, trong khi ios chỉ là một typedef.

Và sự khác biệt là tên của một lớp là thành viên của lớp đó (9/2), và do đó có thể được tra cứu như tên của loại trong InFStream (3.4.1/7 mục 1) dưới dạng nó là thành viên của một lớp cơ sở là InFStream. Nhưng một số typedef chỉ đơn thuần là bên cạnh lớp cơ sở trong một không gian tên khác không thể thấy được.

[Số phần tiêu chuẩn từ C++ 98.]

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