gpt4 book ai didi

c++ - 聪明的 if/else 结构

转载 作者:太空狗 更新时间:2023-10-29 23:43:46 25 4
gpt4 key购买 nike

我有三个变量,如果它们都低于某个x,我想做点什么。如果它们都在上面,我想做其他事情,如果一些变量在下面,一些在上面,我还想做第三件事。

现在我想以最有效的方式做到这一点。当然,我可以先检查是否所有的都在上面,如果不是,检查是否所有的都在下面....

但是有没有更有效的方法,我没有意识到?

感谢你们的投入。

最佳答案

Now I want to do this the most efficient way. For sure, I could first check if all of them are above, if not, check, if all of them are below ....

编写富有表现力的代码

But is there any more efficient way, I didn't recognize?

你不需要。优化器会为您识别它。

如果它看到不变量的重新测试,它会尝试为您重新排序代码(它会成功)。

始终编写具有逻辑意义的表达代码。我看到 gcc 和 clang 的优化器将数百行 c++ 代码(表达的意图)变成一个立即寄存器加载,因为编译器意识到只能有一个结果。

这是一个示例 (c++14):

#include <cstdint>
#include <cstddef>
#include <utility>
#include <type_traits>

//
// library boilerplate
//
namespace detail {
template<class Bool>
constexpr bool all_of(Bool&& b) {
return b;
}

template<class Bool1, class...Rest>
constexpr bool all_of(Bool1&& b1, Rest&&... rest) {
return all_of(std::forward<Bool1>(b1)) and all_of(std::forward<Rest>(rest)...);
}

template<class Bool>
constexpr bool none_of(Bool&& b) {
return not b;
}

template<class Bool1, class...Rest>
constexpr bool none_of(Bool1&& b1, Rest&&... rest) {
return none_of(std::forward<Bool1>(b1)) and none_of(std::forward<Rest>(rest)...);
}

}

template<class...Bools>
constexpr bool all_of(Bools&&... bs)
{
return detail::all_of(std::forward<Bools>(bs)...);
}

template<class...Bools>
constexpr bool none_of(Bools&&... bs)
{
return detail::none_of(std::forward<Bools>(bs)...);
}

//
// external functions
//

void doX();
void doY();
void doZ();

bool testA();
bool testB();
bool testC();

//
// a test
//
void test()
{
const bool a = testA(), b = testB(), c = testC();
if (all_of(a, b, c)) {
doX();
}
else if (none_of(a, b, c)) {
doZ();
}
else {
doY();
}
}

GCC 将其解析为以下汇编语言:

test():                               # @test()
push rbp
push rbx
push rax
call testA()
mov ebp, eax
call testB()
mov ebx, eax
call testC()
test bpl, bpl
je .LBB0_3
and bl, al
cmp bl, 1
jne .LBB0_5
add rsp, 8
pop rbx
pop rbp
jmp doX() # TAILCALL
.LBB0_3:
or bl, al
je .LBB0_4
.LBB0_5:
add rsp, 8
pop rbx
pop rbp
jmp doY() # TAILCALL
.LBB0_4:
add rsp, 8
pop rbx
pop rbp
jmp doZ() # TAILCALL

注意编译器如何使用 ax、bx 和 ebp 寄存器来缓存测试状态。另请注意,在程序集输出的任何位置完全没有调用 any_ofall_of

我们当然可以这样写:

void test2()
{
const bool a = testA(), b = testB(), c = testC();
if (a and b and c) {
doX();
}
else if (not (a or b or c)) {
doZ();
}
else {
doY();
}
}

结果是产生相同的程序集:

test2():                              # @test2()
push rbp
push rbx
push rax
call testA()
mov ebx, eax
call testB()
mov ebp, eax
call testC()
test bl, bl
je .LBB1_3
test bpl, bpl
je .LBB1_3
test al, al
je .LBB1_3
add rsp, 8
pop rbx
pop rbp
jmp doX() # TAILCALL
.LBB1_3:
or bl, bpl
add rsp, 8
or bl, al
je .LBB1_6
pop rbx
pop rbp
jmp doY() # TAILCALL
.LBB1_6:
pop rbx
pop rbp
jmp doZ() # TAILCALL

我们甚至可以这样写:

void test3()
{
const bool a = testA(), b = testB(), c = testC();
if (a) {
if (b) {
if (c) {
doX();
}
else {
doY();
}
}
else {
doY();
}
}
else {
if (b) {
doY();
}
else {
if (c) {
doY();
}
else {
doZ();
}
}
}
}

要生成几乎相同的代码:

test3():                              # @test3()
push rbp
push rbx
push rax
call testA()
mov ebx, eax
call testB()
mov ebp, eax
call testC()
test bl, bl
je .LBB2_4
test bpl, bpl
je .LBB2_3
test al, al
je .LBB2_3
add rsp, 8
pop rbx
pop rbp
jmp doX() # TAILCALL
.LBB2_4:
test bpl, bpl
jne .LBB2_3
test al, al
je .LBB2_6
.LBB2_3:
add rsp, 8
pop rbx
pop rbp
jmp doY() # TAILCALL
.LBB2_6:
add rsp, 8
pop rbx
pop rbp
jmp doZ() # TAILCALL

演示:https://godbolt.org/g/j4q1ke

这个故事的寓意与所有 C++ 故事的寓意相同:“正确、清晰、优雅和简洁地表达自己。然后让优化器完成它的工作。”

gcc、msvc、intel 和 clang 优化器已经被成千上万比我(可能还有你)聪明的人研究过。我们手写的“优化”源代码不可能做得更好。此外,这些编译器的 future 迭代会更好。因此,即使您从未改进过自己的代码,只要您接受更新版本的编译器,它就会自动得到改进。

所以我想这个故事的第二个寓意是(与许多软件经理的信念相反......),“经常升级你的编译器。”

最后的说明:

我们甚至可以变得非常高级,并使用元组来保存条件的结果。这意味着我们正在测试的条件集是 DRY(这很好!)。这也将在 gcc 上生成相同的代码:

namespace detail {
template<class Tup, std::size_t...Is>
bool none_of(Tup&& tup, std::index_sequence<Is...>)
{
bool result = true;
using unwrap = int[];
void(unwrap{0,
(result = result and not std::get<Is>(tup),0)...
});
return result;
}

template<class Tup, std::size_t...Is>
bool all_of(Tup&& tup, std::index_sequence<Is...>)
{
bool result = true;
using unwrap = int[];
void(unwrap{0,
(result = result and std::get<Is>(tup),0)...
});
return result;
}
}

template<class...Bools>
constexpr bool none_of(std::tuple<Bools...> const& tup)
{
constexpr auto N = std::tuple_size<std::tuple<Bools...>>::value;
return detail::none_of(tup, std::make_index_sequence<N>());
}

template<class...Bools>
constexpr bool all_of(std::tuple<Bools...> const& tup)
{
constexpr auto N = std::tuple_size<std::tuple<Bools...>>::value;
return detail::all_of(tup, std::make_index_sequence<N>());
}

void test4()
{
// note, the list of conditions is now expressed only once.
// suddenly our code is trivial to maintain
const auto conditions = std::make_tuple(testA(), testB(), testC());

// all_of has obvious meaning, regardless of how many conditions are in the tuple
if (all_of(conditions)) {
doX();
}
// ditto for none_of
else if (none_of(conditions)) {
doZ();
}
else {
doY();
}
}

关于c++ - 聪明的 if/else 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41649272/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com