boost.boost:tribool类似C++内建的bool类型,但基于三态的的bool逻辑:在true(真)和false(假)之外还有一个indeterminate
(未知、不确定)状态。三态布尔逻辑的一个例子场景是执行某项任务,在执行之前状态是未知的(没有开始也没有结束),启动任务后状态是true,停止任务是false。
tribool位于名字空间boost::logic,但为了方便使用被using语句引入了boost名字空间,需要包含头文件< boost/login/tribool.hpp>,即:
#include <boost/logic/tribool.hpp>
using namespace boost;
类摘要
tribool类很简单,它内部实现了三态bool值的表示。除了构造函数没有什么其他成员函数。可以在创建tribool对象的同时传入三态bool值对它们进行初始化,如果使用无参的默认构造函数,那么tribool默认值是false。
对tribool的操作都是通过逻辑运算符和比较运算符的重组来完成的,支持的逻辑运算包括||
、&&
和!
,比较运算支持==
和!=
,这些操作都可以任意混合bool
和tribool
一起运算。
不确定状态indeterminate
是一个特殊的tribool值,它与bool值true、false的运行遵循三态布尔逻辑:
自由函数indeterminate()可以判断一个tribool是否出于不确定状态。
用法
tribool可以像普通的bool类型一样使用,只是多出了一个indeterminate的取值。如果仅仅使用true和false,那么tribool与bool的用法是完全一样的,但如果需要使用indeterminate值,就必须遵循三态布尔的逻辑运算规则。
#include <iostream>
#include <assert.hpp>
#include <boost/logic/tribool.hpp>
using namespace boost;
int main() {
tribool tb(true); // 值为true的tribool
tribool tb2(!tb);
if(tb){
printf("true\n"); // 输出true
}
tb2 = indeterminate; //tb2是不确定状态
assert(indeterminate(tb2)); //用indeterminate()函数检测状态
if (tb2 == indeterminate){ // 与indeterminate比较,无意义
printf("indeterminate\n"); //不会输出
}
if (indeterminate(tb2)){ // 用indeterminate()函数检测状态
printf("indeterminate()\n"); // 输出indeterminate()
}
printf("%d\n", (tb2 || true)); // 逻辑或状态,输出1
return 0;
}
在处理tribool的不确定状态时要小心,因为它既不是true也不是false,使用它进行条件判断永远不会成立,判断不确定状态必须要使用indeterminate()函数:
#include <iostream>
#include <boost/logic/tribool.hpp>
using namespace boost;
int main() {
tribool tb(indeterminate);
if (tb == indeterminate){
printf("new reach there\n");
}
if (tb){
printf("new reach there\n");
}
if (!tb){
printf("new reach there\n");
}
if (indeterminate(tb)){
printf("indeterminate()\n");
}
return 0;
}
为第三态更名
tribool的第三态类型indeterminate_keyword_t实际上是一个函数指针类型:
typedef bool *(indeterminate_keyword_t)(tribool, detail::indeterminate_t);
而indeterminate实际上只是一个符合indeterminate_keyword_t类型的函数,它的真正功能是用来判断tribool对象内部值是否是第三态,但在tribool的构造函数里被当做一个标志,只要是indeterminate_keyword_t类型那么就置为不确定状态:
tribool(indeterminate_keyword_t): // 构造函数,接受特殊的参数
value(indeterminate_value){} // 置于不确定状态
作为第三态的名字indeterminate很清晰明确但是可能有点长,所以tribool允许把interminate改变成任意用户喜欢的名字,比如unknown,maybe等。
只需要在全局域中使用宏BOOST_TRIBOOL_THIRD_STATE就可以为第三态更名:
#include <iostream>
#include <assert.h>
#include <boost/logic/tribool.hpp>
using namespace boost;
BOOST_TRIBOOL_THIRD_STATE(unknown);
int main() {
tribool tb(unknown); //可以作为不确定值
assert(unknown(tb)); // 可以作为检测函数
assert(unknown(tb || false));
return 0;
}
宏只是定义了一个新的indeterminate_keyword_t类型的函数而已,类似于:
inline bool some_name(tribool x)
{return x.value == tribool::indeterminate_value;}
因为宏BOOST_TRIBOOL_THIRD_STATE实质上定义了一个函数,而C++不允许函数嵌套,所以这个宏最好在全局域中使用,它将在定义后的整个源代码中都生效。
如果把BOOST_TRIBOOL_THIRD_STATE用在一个名字空间里,那么新的第三态名字将作为名字空间的一个成员,使用时需要加上名字空间限定,比如:
namespace tmp_ns{
BOOST_TRIBOOL_THIRD_STATE(unknown);
};
BOOST_TRIBOOL_THIRD_STATE(tmp_ns::unknown);
输入输出
tribool可以像bool类型一样进行流操作,但需要包含另一个头文件<boost/logic/tribool_io.hpp>
#include <iostream>
#include <assert.h>
#include <boost/logic/tribool.hpp>
#include <boost/logic/tribool_io.hpp>
using namespace boost;
int main() {
tribool tb1(false), tb2(true), tb3(indeterminate);
std::cout << tb1 << ", " << tb2 << ", " << tb3; // 0, 1, 2
return 0;
}
其他
optional<bool>
在功能上有些类似tribool,一个未初始化的optional<bool>
同时可以表示不确定的bool值,比如:
#include <iostream>
#include <boost/optional.hpp>
using namespace boost;
int main() {
optional<bool> b; //b未经初始化,既不是true也不能false
if (!b){
printf("interminate\n");
}
b = false;
if(b){ //b有值false
printf("b=%d\n", *b); //输出b=0
}
return 0;
}
但optional<bool>
的语义是未初始化的bool,是无意义的值,而tribool的intdeterminate是已经初始化的有意义值,它表示bool值不确定。这两者存在着细微但十分重要的区别。
由于optional支持bool转换,用于检测optional是否已经初始化,所以在bool语境下如果补助与optional的这个特性很容易导致意外的错误。比如,下面的代码中本意是向使用optional内的bool值作为if语句的判断条件,但实际上判断的是optional未初始化:
optional<bool> b(false);
// 错误写法
if(!b){ // option的bool转换
printf("false\n");
}
//正确写法
if(b && *b){
printf("false\n");
}
选择optional<bool>
还是tribool需要由具体的业务逻辑来决定。如果返回值可能是无效的(不存在有效的返回值),那么就是optional;如果返回值总是确定的,但可能无法确定其意义,那么就用tribool。