使用逻辑与(&&)时,如何以编程方式查看 C++ 中未满足哪些条件?

How to programmatically see which conditions were not met in C++ when using logical AND (&&)?

我正在尝试有效地推导出哪些条件导致 if 语句被程序忽略,而不使用一系列 if 语句来单独验证每个变量的相对完整性。

这可能吗?

bool state = false;
int x = 0;
int y = 1;
int z = 3;

if(x == 0 && y == 1 && z == 2)    {
// Do something...
state == true;
}

if(state == false)    {

std::cout << "I did not execute the if statement because the following 
conditions were not met: " << std::endl;

/*Find a way to make the program output that z != 3 stopped the 
conditional from running without directly using if(z != 2)*/

} 

如果这是您想要向最终用户显示的内容,而不仅仅是在调试时,如评论中所建议的,您可以为自己设计一个简单的数据结构。这将是一个列表/向量/条目数组,每个条目包含 a) 一个要比较的值,b) 一个要测试的值,以及可选的 c) 测试的描述。

然后简单地迭代列表,并检查所有列表是否相等。如果没有,您可以停止程序的运行并打印出描述。

为了更直接地回答您的问题:不,C++ 中没有任何内容可以让您检查先前语句的结果。您在源代码中看到的语句和操作经过编译,甚至可能无法在汇编指令中被轻易识别。能够检查结果意味着数据必须存储在某个地方,这将极大地浪费内存和处理时间。这就是为什么你必须自己做。

您可以在 if 中的每个条件之间引入一个计数器作为 "condition" 以查看何时 short-circuit 运算符 && 的评估禁止执行后者条件:

int nrOfConditionFailing = 1;

if(x == 0 &&
   nrOfConditionFailing++ && y == 1 &&
   nrOfConditionFailing++ && z == 2)    {
    state = true;
}

if (!state) {
  cout << "failed due to condition nr " << nrOfConditionFailing << endl;
}

如果要检查所有条件,您不能一次完成if-statement; Short-circuit 运算符 && 的计算将阻止后一个条件为偶数 checked/evaluated 如果前一个条件的计算结果为 false。

但是,您可以执行这样的检查,作为一个表达式,为每个不满足的条件在 unsigned int 中标记一个位:

int x = 1;
int y = 1;
int z = 3;

unsigned int c1 = !(x == 0);
unsigned int c2 = !(y == 1);
unsigned int c3 = !(z == 2);

unsigned int failures =
  (c1 << 0)
| (c2 << 1)
| (c3 << 2);

if (failures) {
    for(int i=0; i<3; i++) {
        if (failures & (1 << i)) {
            cout << "condition " << (i+1) << " failed." << endl;
        }
    }
}
else {
  cout << "no failures." << endl;
}

Is this possible?

按照你想问题的方式是不可能的。您可以通过 运行 每个测试单独解决您的问题,存储结果,然后确定其中哪些是 false:

std::vector<std::tuple<std::string,bool> > tests = {
       {"x==0",x==0}, // test name as a string followed by the actual test
       {"y==1",y==1},
       {"z==2",z==2}
    };

if(!all_of(tests.begin(),tests.end(),[](std::tuple<std::string,bool> &t) { return std::get<1>(t); }))
{
   std::cout << "The following tests failed: ";
   //remove all tests that passed
   tests.erase(
        std::remove_if(tests.begin(),tests.end(),[](std::tuple<std::string,bool> &t) { return std::get<1>(t); }),
        tests.end());
   //This will only print out the tests that failed
   std::transform(tests.begin(),tests.end(),std::ostream_iterator<std::string>(std::cout, " "),[](std::tuple<std::string,bool> &t) { return std::get<0>(t); });
   std::cout << std::endl;
} else {

  //what to do if all tests were true
} 

这将评估所有测试(即,它不会使用 && 的 short-circuiting)并打印所有失败的测试。您可能会将其包装到 class 中,以使其更具通用性和用户友好性。

原始代码单独测试每个变量。 && 系列完全等同于一系列 if...else 语句。一个与另一个相比并没有什么效率低下的地方,也没有什么 "clever" 可以使用一些棘手的解决方案来实现与直接代码相同的最终结果。

我可能会写:

char const *reason = nullptr;

if(x != 0)
    reason = "x failed";
else if (y != 1)
    reason = "y failed";
else if (z != 2 )
    reason = "z failed";

if ( reason )
    std::cout << reason << '\n';
else
{
    // success code here...
}

我通常会做类似下面的事情来确定一系列有效性检查是否有效并标记哪些失败。

unsigned long ulFlags = 0;
int x = 0;
int y = 1;
int z = 3;

ulFlags |= (x == 0) : 0 ? 0x0001;  // if bit set then condition failed.
ulFlags |= (y == 1) : 0 ? 0x0002;  // if bit set then condition failed.
ulFlags |= (z == 2) : 0 ? 0x0004;  // if bit set then condition failed.

if(ulFlags == 0) {
    // Do something since all conditions are met and valid ...
} else {
    std::cout << "I did not execute if statement because: " << std::hex << ulFlags << std::endl;

    /* Find a way to make the program output that z != 3 stopped the 
      conditional from running without directly using if(z != 2) */
} 

这与其他一些答案的想法相同,但使用模板来简化使用它的语法。将所有单独的检查存储在 std::array<bool, N> 和一个额外的 bool 中,以便能够 re-check 完整的语句而无需再次检查单独的结果。

没有动态分配也是一个优点。

#include <iostream>
#include <array>
#include <type_traits>

template <typename... N>
struct what_failed {
    what_failed(N... n) : arr{n...}, status{(... && n)} {
        static_assert(std::conjunction_v<std::is_same<N, bool>...>, "Only pass bools");
    }
    std::array<bool, sizeof...(N)> arr;
    bool status;
    operator bool() { return status; }
};

int main() {
    auto check = what_failed(2 == 5, 2 < 5, 2 > 5, 1 == 1);
    if (check)
        std::cout << "Check: All true";
    else {
        std::cout << "Check: ";
        for (auto c : check.arr)
            std::cout << c << ' ';
    }
    return 0;
}

由于构造函数中的折叠表达式和模板推导,这需要 c++17,但是对于 c++11 可以使用一些额外的 help-templates.