Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145299

Why is no qualification necessary?

OK, I'll just post the complete program even though it has extraneous stuff and the code in question is the dead code…

#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;
}

This compiles fine with MSVC and g++. But in the InFStream class, why do I not need to qualify ios_base? Or, same question really, why do I need to use std:: qualification of ifstream in the constructor initializer list?

Upvotes: 13

Views: 772

Answers (4)

Totonga
Totonga

Reputation: 4366

When you derive from the class you have specify

std::ifstream

to be able to find the class in the std namespace.

In the code itself you class derived from std::ifstream knows everything of ifstream.

Inheritance of ifstream:

ios_base -> ios -> istream -> ifstream

Upvotes: 0

CB Bailey
CB Bailey

Reputation: 792327

The difference is that ifstream isn't visible as an injected class name because it is the name of a typedef, not the name of the class. It isn't therefore visible unqualified as an injected class name from the base class.

ios_base is a genuine class name which is a base class (of a base class) of the class where it is used and so is visible unqualified as an inject class name.

E.g.

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.
};

In your example, instead of std::ifstream, you can use unqualified basic_ifstream instead. (Or basic_ifstream<char> or basic_ifstream<char, std::char_traits<char> > but these don't really save any typing or help clarity at all.)

Upvotes: 7

John Marshall
John Marshall

Reputation: 7005

Another observation is that ios_base::openmode works, but ios::openmode does not:

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

I think a1ex07 has found the crux of the matter: here again, ios_base is the name of a class, while ios is merely a typedef.

And the difference is that the name of a class is a member of that class (9/2), and so can be looked up as the name of the type in InFStream (3.4.1/7 item 1) as it is a member of a base class of InFStream. But some typedef merely alongside the base class off in some other namespace can't be seen.

[Standard section numbers from C++98.]

Upvotes: 4

a1ex07
a1ex07

Reputation: 37374

Some thoughts about why you have to specify std::ifstream in constructor's initializer . I think typedef is the culprit - ifstream is defined as typedef basic_ifstream<char, char_traits<char> > ifstream;). If you change your constructor to

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

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

you also don't have to specify std::basic_ifstream. I cannot find details about why typedef works this way, but the problem is reproducible. For instance,

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

Upvotes: 6

Related Questions