我用GCC和此函数调用做了一些测试:
add(argc, (argc > 1)?(argv[1][0] > 5)?50:10:1, (argc > 2)?(argv[2][0] > 5)?50:10:1, (argc > 3)?(argv[3][0] > 5)?50:10:1);
生成的带有gcc -O3的汇编代码有35条指令。
我正在浏览一些代码,发现其中有一些三元运算符。这段代码是我们使用的库,应该很快。
我在考虑是否要节省那里的空间。
你有什么经验?
[三元运算符的性能不应与编写良好的等价if
/ else
语句...表现出相同的含义,它们可以很好地解析为抽象语法树中的相同表示形式,并进行相同的优化等。] >
[如果要初始化常量或引用,或者要在成员初始化列表中计算出要使用的值,则不能使用if
/ else
语句,但是可以使用?
:
:] >
const int x = f() ? 10 : 2; X::X() : n_(n > 0 ? 2 * n : 0) { }
简洁代码的编写
使用
?
:
的关键原因包括本地化,并避免重复重复同一语句/函数调用的其他部分,例如:
if (condition) return x; else return y;
...仅比...更可取
return condition ? x : y;
...基于可读性,如果与经验不足的程序员打交道,或者某些术语过于复杂,以致
?
:
结构在噪音中迷失了方向。在更复杂的情况下,例如:fn(condition1 ? t1 : f1, condition2 ? t2 : f2, condition3 ? t3 : f3);
等效
if
/else
:if (condition1) if (condition2) if (condition3) fn(t1, t2, t3); else fn(t1, t2, f3); else if (condition3) fn(t1, f2, t3); else fn(t1, f2, f3); else if (condition2) ...etc...
这是编译器可能优化或可能不优化的很多额外的函数调用。
此外,
?
允许您选择一个对象,然后使用其成员:
(f() ? a : b).fn(g() ? c : d).field_name);
等效的
if
/else
为:if (f()) if (g()) x.fn(c.field_name); else x.fn(d.field_name); else if (g()) y.fn(c.field_name); else y.fn(d.field_name);
命名临时人员是否可以改善上述if / else怪癖?
如果表达式
t1
,f1
,t2
等太冗长而无法重复键入,则创建命名临时对象可能会有所帮助,但随后:
要获得与?
:
匹配的性能,您可能需要使用std::move
,除非在调用的函数中将相同的临时变量传递给两个&&
参数时,则必须避免使用它。这更复杂且容易出错。
[[[[c
?
x:
y得出c,然后要么不能同时使用x]和y,这可以肯定地说在使用指针之前先测试它是否不是nullptr
,同时提供一些后备值/行为。该代码仅具有实际选择的x]和y中的任何一个的副作用。对于命名的临时变量,您可能需要在其if
/ else
周围或在其?
:
的内部进行初始化,以防止执行不需要的代码,或者代码执行次数超出期望。功能差异:统一结果类型void is(int) { std::cout << "int\n"; }
void is(double) { std::cout << "double\n"; }
void f(bool expr)
{
is(expr ? 1 : 2.0);
if (expr)
is(1);
else
is(2.0);
}
[在上述条件运算符版本中,1
经历了对double
的标准转换,因此类型与2.0
相匹配,这意味着即使在is(double)
/true
情况下,也会调用1
重载。if
/else
语句不会触发此转换:true
/1
分支调用is(int)
。
您也不能在条件运算符中使用整体类型为void
的表达式,而它们在if
/ else
下的语句中有效。
强调:需要价值的行动前后的价值选择
if
/ else
语句首先强调分支,而第二要执行的工作,而三元运算符则强调要进行选择的值的选择。
?
:
一起使用,是表达它并继续进行编码“流程”的最小破坏性方法。 我用GCC和此函数调用做了一些测试:
add(argc, (argc > 1)?(argv[1][0] > 5)?50:10:1, (argc > 2)?(argv[2][0] > 5)?50:10:1, (argc > 3)?(argv[3][0] > 5)?50:10:1);
生成的带有gcc -O3的汇编代码有35条指令。
带有if / else +中间变量的等效代码为36。使用嵌套if / else的事实是3> 2> 1,我得到了44。我什至没有尝试将其扩展为单独的函数调用。
现在,我没有执行任何性能分析,也没有对生成的汇编代码进行质量检查,但是在类似这样的简单操作中,没有循环e.t.c.我相信越短越好。
看来三元运算符毕竟有一些价值:-)
当然,只有在代码速度绝对至关重要的情况下。如果嵌套时,if / else语句比(c1)?(c2)?(c3)?(c4)?: 1:2:3:4之类的语句容易读得多。将巨大的表达式作为函数参数很有趣。
还请记住,嵌套三元表达式使代码重构-或通过将一堆方便的printfs()置于条件下进行调试-难度大得多。
const
特别有用:const int foo = (a > b ? b : a - 10);
如果不使用函数cal也无法使用if / else块执行此操作。如果碰巧有很多类似const的情况,您可能会发现,通过使用if / else赋值正确地初始化const会有一点好处。衡量吧!虽然可能甚至无法测量。我倾向于这样做的原因是因为通过将其标记为const,编译器知道以后我何时执行某些操作可能会/会意外地更改我认为已修复的功能。
有效地说,三元运算符对于const正确性很重要,而const正确性是一个很好的习惯:[通过让编译器帮助您发现所犯的错误,这节省了很多时间
想象: 无论如何,三元运算符是某些语言(C,C#,C ++,Java等)的表达式,它们 具有独特的作用x = if (t) a else b
not具有“ if-else”表达式,因此它
我用GCC和此函数调用做了一些测试:
add(argc, (argc > 1)?(argv[1][0] > 5)?50:10:1, (argc > 2)?(argv[2][0] > 5)?50:10:1, (argc > 3)?(argv[3][0] > 5)?50:10:1);
生成的带有gcc -O3的汇编代码有35条指令。
const int foo = (a > b ? b : a - 10);
如果不使用函数cal也无法使用if / else块执行此操作。如果碰巧有很多类似const的情况,您可能会发现,通过使用if / else赋值正确地初始化const会有一点好处。衡量吧!虽然可能甚至无法测量。我倾向于这样做的原因是因为通过将其标记为const,编译器知道以后我何时执行某些操作可能会/会意外地更改我认为已修复的功能。
有效地说,三元运算符对于const正确性很重要,而const正确性是一个很好的习惯:[通过让编译器帮助您发现所犯的错误,这节省了很多时间
想象: 无论如何,三元运算符是某些语言(C,C#,C ++,Java等)的表达式,它们 具有独特的作用x = if (t) a else b
not具有“ if-else”表达式,因此它