奇怪的编译器错误和模板继承

ere*_*eOn 5 c++ templates protected derived safe-bool-idiom

有人可以解释一下为什么这段代码:

class safe_bool_base
{ //13
    protected:

        typedef void (safe_bool_base::*bool_type)() const;

        void this_type_does_not_support_comparisons() const {} //18

        safe_bool_base() {}
        safe_bool_base(const safe_bool_base&) {}
        safe_bool_base& operator=(const safe_bool_base&) { return *this; }
        ~safe_bool_base() {}
};

template <typename T=void> class safe_bool : public safe_bool_base
{
    public:

        operator bool_type() const
        {
            return (static_cast<const T*>(this))->boolean_test() ? &safe_bool_base::this_type_does_not_support_comparisons : 0;
        }

    protected:

        ~safe_bool() {}
};

template <> class safe_bool<void> : public safe_bool_base
{
    public:

        operator bool_type() const
        {
            return (boolean_test() == true) ? &safe_bool_base::this_type_does_not_support_comparisons : 0; //46
        }

    protected:

        virtual bool boolean_test() const = 0;
        virtual ~safe_bool() {}
};
Run Code Online (Sandbox Code Playgroud)

产生以下编译器错误?

c:\project\include\safe_bool.hpp(46) : error C2248: 'safe_bool_base::this_type_does_not_support_comparisons' : cannot access protected member declared in class 'safe_bool_base'
c:\project\include\safe_bool.hpp(18) : see declaration of 'safe_bool_base::this_type_does_not_support_comparisons'
c:\project\include\safe_bool.hpp(13) : see declaration of 'safe_bool_base'
Run Code Online (Sandbox Code Playgroud)

由于两个safe_bool模板都来自safe_bool_base,我不明白为什么一个人无法访问基类的受保护成员.

我错过了什么吗?

Chu*_*dad 9

这应该有所帮助(在非模板情况下也可以重现)

struct A{
protected:
    void f(){}
};

struct B : A{
    void g(){&A::f;}        // error, due to Standard rule quoted below
};

int main(){
}
Run Code Online (Sandbox Code Playgroud)

VS给出''A :: f':无法访问在'A'类中声明的受保护成员"

对于相同的代码,Comeau给出了

"ComeauTest.c",第7行:错误:受保护的函数"A :: f"(在第3行声明)无法通过"A"指针或对象访问void g(){&A :: f;} ^

"ComeauTest.c",第7行:警告:表达式没有效果void g(){&A :: f;}

这是实现所需意图的固定代码

struct A{
protected:
    void f(){}
};

struct B : A{
    void g(){&B::f;}        // works now
};

int main(){
}
Run Code Online (Sandbox Code Playgroud)

那么,为什么第一个代码片段不起作用?

这是因为C++ Standard03中的以下规则

11.5/1-"当派生类的朋友或成员函数引用受保护的非静态成员函数或受保护的基类的非静态数据成员时,除了前面第11.102节中描述的那些之外,还应用访问检查.)除非形成指向成员(5.3.1)的指针,访问必须通过指向,引用或派生类本身的对象(或从该类派生的任何类)(5.2.5).如果访问是形成一个指向成员的指针,nested-name-specifier应命名派生类(或从该类派生的任何类).

因此,如下更改操作员函数内的返回值

return (boolean_test() == true) ? &safe_bool<void>::this_type_does_not_support_comparisons : 0; //46 

return (static_cast<const T*>(this))->boolean_test() ? &typename safe_bool<T>::this_type_does_not_support_comparisons : 0; 
Run Code Online (Sandbox Code Playgroud)

编辑2:请忽略我的解释.大卫是对的.以下是它归结为的内容.

struct A{
protected:
    int x;
};

struct B : A{
    void f();
};

struct C : B{};

struct D: A{            // not from 'C'
};

void B::f(){
    x = 2;         // it's own 'A' subobjects 'x'. Well-formed

    B b;
    b.x = 2;       // access through B, well-formed

    C c;
    c.x = 2;       // access in 'B' using 'C' which is derived from 'B', well-formed.

    D d;
    d.x = 2;       // ill-formed. 'B' and 'D' unrelated even though 'A' is a common base
}

int main(){} 
Run Code Online (Sandbox Code Playgroud)

  • @Chubsdad:我不确定我是否遵循你的例子,理性是正确的,因为允许它会破坏受保护的访问限定符,而不是与其他类型B的对象有关但与其他类型有关.考虑`struct A {...}; 结构B:A {...}; struct C:A {...}; void B :: break(X&x){x.*(&X :: f)(); }`.如果`X`是'B`,代码应该编译并工作,但如果`X`是'A`,那么可以使用`C`实例(与`B`无关)调用该方法,它将调用来自不相关类的非公开成员. (2认同)
  • ...通过强制您将指针指向最派生类型,它确保您不能这样做.`C c; void B :: break(){c.*(&B :: f)(); 当成员指针指向"B"而不是"C"时,将产生编译错误. (2认同)