27

C# null 合并运算符是否有 C++ 等价物?我在我的代码中做了太多的空检查。因此正在寻找一种方法来减少空代码的数量。

4

8 回答 8

15

我刚发现这个:?? 运算符又名空合并运算符

您还可以在 C/C++ 中使用?: 操作符将其作为 GNU 扩展:

string pageTitle = getTitle() ?: "Default Title";
于 2014-01-06T14:28:29.110 回答
14

在 C++ 中默认情况下没有办法做到这一点,但你可以写一个:

在 C# 中的 ?? 运算符定义为

a ?? b === (a != null ? a : b)

所以,C++ 方法看起来像

Coalesce(a, b) // put your own types in, or make a template
{
    return a != null ? a : b;
}
于 2009-11-23T19:34:20.143 回答
5

使用模板和 C++11 lambda:

template<typename TValue, typename TRhsEvaluator>
TValue coalesce(TValue lhsValue, TRhsEvaluator evaluateRhs) {
     return lhsValue ? lhsValue : evaluateRhs();
}
  • 第一个参数(左侧)只计算一次。
  • 仅当第一个参数为假时才评估第二个参数(右侧)。

请注意,if并将?提供的表达式静态转换为bool,并且指针具有内置explicit operator bool() const运算符,该运算符等于!= nullptr

示例用法:

void * const      nonZeroPtr = reinterpret_cast<void *>(0xF);
void * const otherNonZeroPtr = reinterpret_cast<void *>(0xA);

std::cout << coalesce(nonZeroPtr, [&] () {
    std::cout << "Side-effect. Should never be printed" << std::endl;
    return otherNonZeroPtr;
}) << std::endl;

上面的代码只会打印0xf到控制台。

右手边需要被包裹在一个 lambda 中——我们不能避免那个样板。实际上,该语言应该提供开箱即用的空合并运算符。

于 2015-02-13T12:10:36.177 回答
4

只想通过概括模板并添加辅助宏来减少 lambda 样板来扩展@Samuel Garcia 的答案:

#include <utility>

namespace coalesce_impl
{
    template<typename LHS, typename RHS>
    auto coalesce(LHS lhs, RHS rhs) ->
        typename std::remove_reference<decltype(lhs())>::type&&
    {
        auto&& initialValue = lhs();
        if (initialValue)
            return std::move(initialValue);
        else
            return std::move(rhs());
    }

    template<typename LHS, typename RHS, typename ...RHSs>
    auto coalesce(LHS lhs, RHS rhs, RHSs ...rhss) ->
        typename std::remove_reference<decltype(lhs())>::type&&
    {
        auto&& initialValue = lhs();
        if (initialValue)
            return std::move(initialValue);
        else
            return std::move(coalesce(rhs, rhss...));
    }
}

#define COALESCE(x) (::coalesce_impl::coalesce([&](){ return ( x ); }))
#define OR_ELSE     ); }, [&](){ return (

使用宏,您可以:

int* f();
int* g();
int* h();

int* x = COALESCE( f() OR_ELSE g() OR_ELSE h() );

我希望这有帮助。

于 2017-04-26T05:27:28.587 回答
2

有一个 GNU GCC 扩展允许使用?:缺少中间操作数的运算符,请参阅Conditionals with Omitted Operands

条件表达式中的中间操作数可以省略。那么如果第一个操作数非零,它的值就是条件表达式的值。

因此,表达式

x ? : y

xif that is nonzero 的值;否则, 的值 y

这个例子完全等同于

x ? x : y

在这种简单的情况下,省略中间操作数的能力并不是特别有用。当它变得有用时,第一个操作数确实或可能(如果它是宏参数)包含副作用。然后在中间重复操作数将执行两次副作用。省略中间操作数会使用已经计算的值,而不会产生重新计算的不良影响。

clang也支持这个扩展。但是,在使用扩展之前,您应该检查您正在使用的编译器和代码的可移植性要求。值得注意的是,MSVC C++ 编译器不支持?:.

另请参阅此处的相关 StackOverflow 讨论。

于 2019-08-19T19:27:55.000 回答
2

这里有两个宏来分别复制?.??运算符。他们都只安全地评估操作数一次b,如果a为空,则完全跳过评估。他们还使用 lambda 语法限制这些评估的范围,并确保没有任何问题:

#define COACALL(a, b) ([&](){ auto val = (a); if (val) (val->b); }());
#define COALESCE(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }()) 

像这样使用:

COACALL( goodPtr, sayHello() );
COACALL( nullPtr, sayHello() );

COALESCE( nullPtr, goodPtr )->sayHello();
COALESCE( nullPtr, COALESCE( nullPtr, goodPtr ) )->sayHello();

COACALL( COALESCE( nullPtr, goodPtr ), sayHello() );
于 2020-02-21T02:02:28.487 回答
1

这个怎么样?

#define IFNULL(a,b) ((a) == null ? (b) : (a))
于 2009-11-23T19:34:59.580 回答
0

只是为了添加提到?:运算符(“猫王运算符”)的答案:我有时将辅助函数与此运算符一起使用,以获取指针或std::optional“包装”值并进行布尔转换的类似类型的基础值表示值的存在。例如:

template <typename T>
constexpr T coalesce (std::optional<T> opt) {
    return *opt;
}
template <typename T>
constexpr T coalesce (T fallback) {
    return fallback;
}

std::optional<int> opt1{5};
std::optional<int> opt2;
int val1 = coalesce(opt1 ?: 0);
int val2 = coalesce(opt2 ?: 0);

唯一的缺点是这必须小心使用,不会给你一个正确使用的静态检查。例如,您可以coalesce(opt2)不使用?: fallbackand 这与*opt2不首先检查它是否包含任何内容相同。所以这个名字coalesce有点误导,但在我看来,如果使用得当,它看起来不言自明(而且非常整洁)。

于 2021-03-12T08:39:46.127 回答