C++ 常量正确性与安全布尔习语
我还有一个问题与safe bool成语有关: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
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的结果进行比较”。任何以这种方式使用私有类型的人都应该得到即将到来的一切。在我看来,它已经过时了