Проверьте, не является ли только одна строковая переменная не nullptr в С++

У меня есть три строковые переменные LPCWSTR, называемые A, B, C.

Я назначаю их из другой функции, которая иногда может возвращать nullptr, если что-то пойдет не так. например:

A = MyFunc();
B = MyFunc();
C = MyFunc();

Теперь для некоторых вещей с этими переменными мне нужно проверить, не является ли только одна из этих переменных nullptr (назначается только одна из переменных).

Я попытался сделать это сам, как:

if ((A == nullptr) && (B == nullptr) && (C <> nullptr)) {}

Любые идеи о том, как это сделать, приветствуются.

Ответ 1

Достаточно легко:

int numSet = 0;
A = MyFunc(); if (A != nullptr) numSet++;
B = MyFunc(); if (B != nullptr) numSet++;
C = MyFunc(); if (C != nullptr) numSet++;
if (numSet == 1) // only one is set

Вы также можете инкапсулировать поведение с помощью вспомогательной функции:

LPCWSTR MyFuncWithCount(int &countSetProperly) {
    LPCWSTR retVal = MyFunc();
    if (retVal != nullptr) countSetProperly++;
    return retVal;
}

int numSet = 0;
A = MyFuncWithCount(numSet);
B = MyFuncWithCount(numSet);
C = MyFuncWithCount(numSet);
if (numSet == 1) // only one is set

В следующем шаге оттуда будет использоваться цикл, основанный на диапазоне, в сочетании с битым списком инициализации в соответствии со следующей полной программой:

#include <iostream>
#include <vector>

typedef void * LPCWSTR;  // Couldn't be bothered including Windows stuff :-)

int main() {
    // Only set two for test purposes.

    LPCWSTR A = nullptr, B = nullptr, C = nullptr;
    LPCWSTR D = &A,      E = nullptr, F = &A;

    int numSet = 0;
    for (const auto &pointer: {A, B, C, D, E, F})
        if (pointer != nullptr)
            numSet++;

    std::cout << "Count is " << numSet << std::endl;
}

Или вы могли бы использовать современный С++ во всей своей красе, используя лямбда-функции, в соответствии со следующим:

#include <iostream>
#include <vector>

typedef void * LPCWSTR;  // Couldn't be bothered including Windows stuff :-)

int main() {
    // Only set two for test purposes.

    LPCWSTR A = nullptr, B = nullptr, C = nullptr;
    LPCWSTR D = &A,      E = nullptr, F = &A;

    int numSet = 0;
    [&numSet](const std::vector<LPCWSTR> &pointers) {
        for (const auto &pointer: pointers)
            if (pointer != nullptr)
                numSet++;
    } (std::vector<LPCWSTR>{A,B,C,D,E,F});

    std::cout << "Count is " << numSet << std::endl;
}

Вероятно, для вашего конкретного случая может быть излишним: -)

Ответ 2

С помощью std вы можете:

const auto vars = {A, B, C}; // Create initializer list.
const bool onlyOneNotNull =
    (std::count(vars.begin(), vars.end(), nullptr) == (vars.size() - 1);
// then you may use find_if to retrieve the non null variable.

Ответ 3

Вот один простой способ:

int not_null = 0;
not_null += A != nullptr;
not_null += B != nullptr;
not_null += C != nullptr;
if (not_null == 1) {
    /* Do stuff */
}

Проверяйте каждое значение nullptr и увеличивайте счетчик, если это не так. Если счетчик заканчивается как 1 в конце, сделайте свою вещь.

Ответ 4

В С++ для обратной совместимости с C возвращаемое значение реляционного оператора равно int, равному 0 или 1. Итак, вы можете сделать:

if ( (a != nullptr) + (b != nullptr) + (c != nullptr) == 1 )

Если вы хотите использовать логические операторы только как логические операторы, есть также дизъюнктивная нормальная форма и конъюнктивная нормальная форма, хотя и с большим количеством операций.

if ( (a && !b && !c) || (!a && b && !c) || (!a && !b && c) )

 

if ( (a || b || c) && (!a || !b) && (!a || !c) && (!b || !c) )

Первое нетрудно прочитать в этом простом случае по сравнению с большинством других решений, хотя оно быстро будет слишком многословным, если бы было более возможные решения.

Вы также можете вставлять их в любой контейнер, например std::array<LPCWSTR, 3>, и делать std::count( pointers.begin(), pointers.end(), nullptr) (как предлагал Jarod42).

Ответ 5

Я не большой поклонник использования таких методов, как, например, в целом, но вы можете использовать тот факт, что для любого указателя ptr, который !!ptr оценивает 0 для нулевого указателя и 1 для непустого указатель на запись

if (!!A + !!B + !!C == 1) {
    ...
}

как плотный способ заставить это работать. Это по сути то же самое, что и решение @Davislor, но с более компактной проверкой "проверить, если null".

Этот подход не масштабируется почти так же, как и принятый подход, и это сложнее читать, но в зависимости от вашей аудитории и кто читает код, он может сделать трюк красиво.