C++ 常量正确性与安全布尔习语

C++ 常量正确性与安全布尔习语,c++,constants,member-function-pointers,const-correctness,safe-bool-idiom,C++,Constants,Member Function Pointers,Const Correctness,Safe Bool Idiom,我还有一个问题与safe bool成语有关: typedef void (Testable::*bool_type)() const; // const necessary? void this_type_does_not_support_comparisons() const {} // const necessary? operator bool_type() const { return ok_ ? &Testable::this_type_d

我还有一个问题与safe bool成语有关:

typedef void (Testable::*bool_type)() const;             // const necessary?
void this_type_does_not_support_comparisons() const {}   // const necessary?

operator bool_type() const
{
    return ok_ ? &Testable::this_type_does_not_support_comparisons : 0;
}
为什么
bool\u类型
(typedef)和
此类型不支持比较
const
? 无论如何,没有人应该通过返回指针实际调用成员函数,对吗? 此处是否需要
const
?否则,
运算符bool\u type(成员函数)会违反常量正确性吗

8.3.5/A cv限定符seq应仅为以下功能类型的一部分: 非静态成员函数,指针指向的函数类型 成员引用,或函数类型定义的顶级函数类型 宣言。cv限定符seq在函数声明符中的作用 与在函数顶部添加cv限定不同 类型,即不创建cv限定的函数类型

如果我读得正确,您可以在常量成员函数中返回指向非常量成员的指针。你不能用非常量对象来调用它

禁止呼叫的一种方法是:

private:
    struct private_ 
    {
        void this_type_does_not_support_comparisons() {}
    };

public:
    typedef void (private_::*bool_type)() const;

    operator bool_type() const
    {
        return ok_ ? &private_::this_type_does_not_support_comparisons : 0;
    }
仍然可以比较指向成员函数的指针是否相等。您必须编写一个
操作符==
操作符=用于触发错误的
Testable::bool_type
类型。使用安全bool习惯用法的CRTP形式更容易,因为这些操作符成为模板,所以可能有错误的主体

例如:

template <typename T>
class safe_bool_concept
{
    // Implementation detail of safe bool
protected:
    ~safe_bool_concept() {}

public:
    operator safe_bool() const
    {
        return static_cast<const T*>(this)->is_null() ? ...;
    }
};

struct Foo : safe_bool_concept<Foo>
{
    ...

private:
    friend class safe_bool_concept<Foo>;
    bool is_null() const { ... }
};
这意味着,如果您想禁止比较,则应该通过CRTP实现安全bool习惯用法。然而,与零的比较仍然有效

如果您选择非会员功能路线,则必须提供
=

安全布尔成语是对以下问题的技术回答:“我想要一辆既有跑车又有拖拉机的车,可能还有一艘船”。实际答案不是技术答案

这就是说,它解决的问题只是给出一个可转换为
bool
的结果,而不是其他任何东西(否则类的实例可以作为实际参数传递,例如,形式参数是
int
)。数据指针可以转换为
void*
。函数指针不是,至少正式地在C++标准内(POSIX是其他的,也练习)。
使用成员函数指针可以防止意外调用该函数,因为安全布尔运算符提供了指针。
const
将其压缩了一点,但是如果命运让某人犯了最多愚蠢的错误,那么此人可能仍然能够调用donothing函数。我想我应该让它有一个私有类型的参数,而不是
const
,其他代码不能提供这样的参数,这样它就不必再是一个愚蠢的成员函数类型了

可以如下所示:

#include <stdio.h>

class Foo
{
private:
    enum PrivateArg {};
    typedef void (*SafeBool)( PrivateArg );
    static void safeTrue( PrivateArg ) {}

    bool    state_;

public:
    Foo( bool state ): state_( state ) {}

    operator SafeBool () const
    { return (state_? &safeTrue : 0); }
};

int main()
{
    if( Foo( true ) ) { printf( "true\n" ); }
    if( Foo( false ) ) { printf( "false\n" ); } // No output.

    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}
#include <stdio.h>

class Foo
{
private:
    bool    isEmpty_;

public:
    Foo( bool asInitiallyEmpty )
        : isEmpty_( asInitiallyEmpty )
    {}

    bool isEmpty() const { return isEmpty_; }
};

int main()
{
    if( Foo( true ).isEmpty() ) { printf( "true\n" ); }
    if( Foo( false ).isEmpty() ) { printf( "false\n" ); } // No output.

    //bool const x0 = Foo( false );       // No compilado!
    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}
#包括
福班
{
私人:
enum PrivateArg{};
typedef void(*SafeBool)(PrivateArg);
静态void safeTrue(PrivateArg){}
布尔州;
公众:
Foo(布尔州):州(州{}
运算符SafeBool()常量
{return(state_?&safeTrue:0);}
};
int main()
{
if(Foo(true)){printf(“true\n”);}
if(Foo(false)){printf(“false\n”);}//无输出。
//int const x1=Foo(false);//不编译!
//void*constx2=Foo(false);//不编译!
}
当然,实际的答案是这样的:

#include <stdio.h>

class Foo
{
private:
    enum PrivateArg {};
    typedef void (*SafeBool)( PrivateArg );
    static void safeTrue( PrivateArg ) {}

    bool    state_;

public:
    Foo( bool state ): state_( state ) {}

    operator SafeBool () const
    { return (state_? &safeTrue : 0); }
};

int main()
{
    if( Foo( true ) ) { printf( "true\n" ); }
    if( Foo( false ) ) { printf( "false\n" ); } // No output.

    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}
#include <stdio.h>

class Foo
{
private:
    bool    isEmpty_;

public:
    Foo( bool asInitiallyEmpty )
        : isEmpty_( asInitiallyEmpty )
    {}

    bool isEmpty() const { return isEmpty_; }
};

int main()
{
    if( Foo( true ).isEmpty() ) { printf( "true\n" ); }
    if( Foo( false ).isEmpty() ) { printf( "false\n" ); } // No output.

    //bool const x0 = Foo( false );       // No compilado!
    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}
#包括
福班
{
私人:
布尔是空的;
公众:
Foo(bool最初是空的)
:isEmpty(最初是空的)
{}
bool isEmpty()常量{return isEmpty;}
};
int main()
{
if(Foo(true).isEmpty(){printf(“true\n”);}
if(Foo(false).isEmpty(){printf(“false\n”);}//无输出。
//bool const x0=Foo(false);//不!
//int const x1=Foo(false);//不编译!
//void*constx2=Foo(false);//不编译!
}
总结报告。提出的问题:

  • 为什么布尔类型(typedef)和这个类型不支持常量比较
有人不太明白他们写了什么。或者他们想限制一点打电话的能力。但是,这样做是徒劳的

  • 无论如何,没有人应该通过返回指针实际调用成员函数,对吗

  • 这里需要常数吗
没有

  • 否则,运算符bool_类型(成员函数)会违反常量正确性吗
没有


干杯&hth.,

我打赌
const
是不必要的。函数的主体也不是,因为你无论如何都不应该调用函数。@Alex:但是没有主体的成员函数没有地址,对吗?至少在VC10上没有主体是不会编译的。嗯,你说得对。我刚刚检查了我的
safe\u bool
模板,该模板深入我的VS2010当前项目,它有一个主体。它也有
const
“我想我应该让它有一个私有类型的参数”,哦,等等,我收回我之前的陈述,这太棒了!我把
常量写错了
,不知怎么搞混了黑白。我只是把它划掉。很抱歉,剩下的都没问题。您仍然可以(愚蠢地,但这是练习的重点)声明一个带有签名的自由函数
void(Foo::PrivateArg)
,并将其与
操作符SafeBool
的结果进行比较。我认为
SafeBool
类型毕竟必须是一个指向成员函数的指针。@Alexandre C:关于您的评论,“您仍然可以(愚蠢地,但这是练习的重点)声明一个带有签名void(Foo::PrivateArg)的自由函数,并分配其地址或将其与运算符SafeBool的结果进行比较”。任何以这种方式使用私有类型的人都应该得到即将到来的一切。在我看来,它已经过时了