Reputation: 145299
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
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
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
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
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