C# null 合并运算符是否有 C++ 等价物?我在我的代码中做了太多的空检查。因此正在寻找一种方法来减少空代码的数量。
8 回答
我刚发现这个:?? 运算符又名空合并运算符
您还可以在 C/C++ 中使用
?:
操作符将其作为 GNU 扩展:string pageTitle = getTitle() ?: "Default Title";
在 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;
}
使用模板和 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 中——我们不能避免那个样板。实际上,该语言应该提供开箱即用的空合并运算符。
只想通过概括模板并添加辅助宏来减少 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() );
我希望这有帮助。
有一个 GNU GCC 扩展允许使用?:
缺少中间操作数的运算符,请参阅Conditionals with Omitted Operands。
条件表达式中的中间操作数可以省略。那么如果第一个操作数非零,它的值就是条件表达式的值。
因此,表达式
x ? : y
有
x
if that is nonzero 的值;否则, 的值y
。这个例子完全等同于
x ? x : y
在这种简单的情况下,省略中间操作数的能力并不是特别有用。当它变得有用时,第一个操作数确实或可能(如果它是宏参数)包含副作用。然后在中间重复操作数将执行两次副作用。省略中间操作数会使用已经计算的值,而不会产生重新计算的不良影响。
clang也支持这个扩展。但是,在使用扩展之前,您应该检查您正在使用的编译器和代码的可移植性要求。值得注意的是,MSVC C++ 编译器不支持?:
.
另请参阅此处的相关 StackOverflow 讨论。
这里有两个宏来分别复制?.
和??
运算符。他们都只安全地评估操作数一次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() );
这个怎么样?
#define IFNULL(a,b) ((a) == null ? (b) : (a))
只是为了添加提到?:
运算符(“猫王运算符”)的答案:我有时将辅助函数与此运算符一起使用,以获取指针或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)
不使用?: fallback
and 这与*opt2
不首先检查它是否包含任何内容相同。所以这个名字coalesce
有点误导,但在我看来,如果使用得当,它看起来不言自明(而且非常整洁)。