为什么没有必要的资格?

时间:2011-10-23 17:58:29

标签: c++

好的,我会发布完整的程序,即使它有无关的东西,而且有问题的代码是死代码......

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

使用MSVC和g ++可以很好地编译。但是在InFStream课程中,为什么我不需要资格ios_base?或者,同样的问题,为什么我需要在构造函数初始化列表中使用std:: ifstream限定条件?

4 个答案:

答案 0 :(得分:7)

不同之处在于ifstream作为注入的类名不可见,因为它是typedef的名称,而不是class的名称。因此,它不能作为基类中的注入类名称显示为不合格。

ios_base是一个真正的类名,它是使用它的类的基类(基类),因此作为注入类名可见不合格。

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

在您的示例中,您可以使用不合格的std::ifstream代替basic_ifstream。 (或basic_ifstream<char>basic_ifstream<char, std::char_traits<char> >但这些并不能真正保存任何打字或帮助澄清。)

答案 1 :(得分:6)

关于为什么必须在构造函数的初始化程序中指定std::ifstream的一些想法。 我认为typedef是罪魁祸首 - ifstream定义为typedef basic_ifstream<char, char_traits<char> > ifstream;)。如果将构造函数更改为

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

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

您也不必指定std::basic_ifstream。我无法找到有关typedef为什么以这种方式工作的详细信息,但问题是可重现的。例如,

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

答案 2 :(得分:4)

另一个观察是ios_base :: openmode有效,但ios :: openmode没有:

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

我认为a1ex07已经找到了问题的症结所在:同样,ios_base是一个类的名称,而ios只是一个typedef。

不同之处在于类的名称是该类的成员(9/2),因此可以在InFStream中查找类型的名称(3.4.1 / 7项) 1)因为它是InFStream的基类的成员。但是有些typedef仅仅与其他命名空间中的基类并排在一起。

[来自C ++ 98的标准部分编号。]

答案 3 :(得分:0)

从类中派生时,您已指定

std::ifstream

能够在std名称空间中找到该类。

在代码本身中,从std :: ifstream派生的类知道ifstream的所有内容。

ifstream的继承:

ios_base -> ios -> istream -> ifstream