这是我用C ++编写的一些代码。有一个addAVP()函数的调用
dMessage.addAVP(AVP_DESTINATION_HOST, peer->getDestinationHost() || peer->getHost());
有两个版本:第二个参数重载为addAVP(int, char*)
,另一个重载为addAVP(int, int)
。我发现我使用的C ++编译器调用addAVP(int, int)
版本,这不是我想要的,因为getDestinationHost()
和getHost()
都返回char*
。
尽管如此||运算符是定义为返回bool所以我可以看到我的错误在哪里。 Bool以某种方式计算为一个整数,这将完全编译并调用第二个addAVP()
。
最近我使用了很多动态类型的语言,即lisp,上面的代码是正确的,可以毫无后顾之忧地编写。很明显,C ++中的上述代码显然是一个很大的错误,但仍有一些问题:
我是否应该使用这种快捷方式,即在C ++中使用|| -operator的返回值。这个编译器是否依赖?
想象一下,我真的非常 编写好的a || b
语法,这可以在C ++中干净利落地完成吗?通过编写运算符重定义?不失性能?
作为我原始请求的后续内容,或者我自己对2的回答:-)我正在考虑使用类来封装(邪恶?)rawpointer:
class char_ptr_w { const char* wrapped_; public: char_ptr_w(const char* wrapped) : wrapped_(wrapped) {} char_ptr_w(char_ptr_w const& orig) { wrapped_=orig.wrapped(); } ~char_ptr_w() {} inline const char* wrapped() const { return wrapped_; } }; inline char_ptr_w operator||(char_ptr_w &lhs, char_ptr_w& rhs) { if (lhs.wrapped() != NULL) return char_ptr_w(lhs.wrapped()); else return char_ptr_w(rhs.wrapped()); };
然后我可以使用:
char_ptr_w a(getDestinationHost()); char_ptr_w b(getHost()); addAVP(AVP_DESTINATION_HOST, a || b);
addAVP
将为char_ptr_w
重载a?b:c
。根据我的测试,这最多生成与三元{{1}}解决方案相同的汇编代码,特别是因为运算符中的NRVO优化,而不,在大多数编译器中,调用副本-constructor(虽然你有包含它)。
当然,在这个特定的例子中,我同意三元解决方案是最好的。我也同意操作员重新定义是谨慎使用的,并不总是有益的。但是在C ++意义上,使用上述解决方案有什么概念上的错误吗?
答案 0 :(得分:7)
在C ++中使逻辑运算符重载是合法的,但只有当一个或两个参数属于类类型时,无论如何它都是一个非常糟糕的主意。 重载逻辑运算符不会短路,因此这可能会导致程序中其他位置显然有效的代码崩溃。
return p && p->q; // this can't possibly dereference a null pointer... can it?
答案 1 :(得分:5)
正如你所发现的,bool实际上是一个int。编译器正在为您的足迹选择正确的函数。如果您想保留类似的语法,可以尝试
char*gdh=0; dMessage.addAVP(AVP\_DESTINATION\_HOST, (gdh=peer->getDestinationHost()) ? gdh : peer->getHost());
我强烈建议不要重新定义运营商。从维护的角度来看,这很可能会使后来的开发人员感到困惑。
答案 2 :(得分:4)
为什么在两个char指针上使用“or”运算符?
我假设peer-> getDestinationHost()或peer-> getHost()可以返回NULL,并且您正在尝试使用返回有效字符串的那个,对吗?
在这种情况下,您需要单独执行此操作:
char *host = peer->getDestinationHost();
if(host == NULL)
host = peer->getHost();
dMessage.addAVP(AVP\_DESTINATION\_HOST, host);
将布尔值传递给需要char *的函数是没有意义的。
在C ++中||返回一个bool,而不是它的一个操作数。打击这种语言通常是一个坏主意。
答案 3 :(得分:2)
1)我是否应该使用这种快捷方式,即在C ++中使用|| -operator的返回值。这个编译器是否依赖?
它不依赖于编译器,但它与||的作用不同运算符使用JavaScript或or
等常见的lisp语言。它将第一个操作数强制转换为布尔值,如果该操作数为true,则返回true。如果第一个操作数为false,则计算第二个操作数并将其强制转换为布尔值,并返回此布尔值。
所以它的作用与( peer->getDestinationHost() != 0 ) || ( peer->getHost() != 0 )
相同。此行为不依赖于编译器。
2)想象一下,我真的,真的不得不写出好的|| b语法,这可以在C ++中干净利落地完成吗?通过编写运算符重定义?不失性能?
由于你使用指向chars的指针,你不能重载运算符(重载需要一个类类型的正式参数,并且你有两个指针)。等效语句C ++将第一个值存储在一个临时变量中,然后使用?:
三元运算符,或者你可以用内联函数来计算第一个表达式两次的成本。
答案 4 :(得分:1)
您可以改为执行以下操作:
dMessage.addAVP(AVP_DESTINATION_HOST,(peer-> getDestinationHost())?peer-> getDestinationHost():peer-> getHost());
这不像||那样整洁但接近它。
答案 5 :(得分:1)
嗯,你的代码问题是对的:a || b将返回一个bool,它被转换为int(0表示false,!= 0表示true)。
关于你的问题:
我不确定返回值是否实际在标准中定义,但我不会使用||的返回值在除了布尔之外的任何情况下(因为它不会很清楚)。
我会用的吗?而不是运营商语法是:(表达式)? (如果为true则执行):(如果为false则执行)。所以在你的情况下,我写道:( peer-> getDestinationHost()=!NULL)? peer-> getDestinationHost():peer-> getHost()。当然,这将调用getDestinationHost()两次,这可能是不可取的。如果不是,那么你将不得不保存getDestinationHost()的返回值,在这种情况下,我只是忘记简短而整洁,只需在函数调用之外使用一个普通的旧“if”。这是保持其工作,效率,最重要的是,可读的最佳方式。