【发布时间】:2010-12-19 14:48:00
【问题描述】:
C# 空合并运算符是否有 C++ 等价物?我在我的代码中做了太多的空检查。所以一直在寻找一种方法来减少空代码的数量。
【问题讨论】:
-
在 GCC 中,它是
a ?: b,但它是不可移植的。
标签: c# c++ null null-coalescing-operator
C# 空合并运算符是否有 C++ 等价物?我在我的代码中做了太多的空检查。所以一直在寻找一种方法来减少空代码的数量。
【问题讨论】:
a ?: b,但它是不可移植的。
标签: c# c++ null null-coalescing-operator
我刚刚发现了这个:The ?? operator aka the Null Coalescing Operator
您还可以在 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;
}
【讨论】:
p = Coalesce(p, new int(10));。此外,在 C# 中,右手操作数似乎不能为 NULL(不能在编译时检查,因为 C++ 中没有可为空的类型?)。另一方面:C++ 中是否需要它,以至于你不能只输入a ? a : b;?
nullptr。无论如何,C/C++ 中没有 null,在某些头文件中只定义了 NULL。 stackoverflow.com/questions/1282295/what-exactly-is-nullptr
A??B??C 的情况下,B??C 是第一个?? 的右手操作数。换句话说,它相当于A??(B??C)。此外,没有限制右手运算符不能为空,它只会导致整个事情为空。
使用模板和 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;否则,值y.这个例子完全等价于
x ? x : y在这个简单的例子中,省略中间操作数的能力 不是特别有用。当它变得有用时是第一个 操作数确实或可能(如果它是宏参数)包含边 影响。然后在中间重复操作数将执行 副作用两次。省略中间操作数已使用该值 在没有重新计算的不良影响的情况下计算。
clang 也支持此扩展。但是,在使用扩展之前,您应该检查您正在使用的编译器和代码的可移植性要求。值得注意的是,MSVC C++ 编译器不支持?: 中的省略操作数。
另请参阅 StackOverflow 相关讨论 here。
【讨论】:
这里有两个宏来分别复制?. 和?? 运算符。如果a 为空,它们都只安全地评估操作数一次并完全跳过评估b。他们还使用 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))
【讨论】:
b 不会被评估,除非a 为空),而函数的参数总是被评估。
a 将永远不会被评估两次。 #define IFNULL(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }())
只是添加到提到?: 运算符(“猫王运算符”)的答案:我有时将辅助函数与此运算符一起使用,以获取指针或std::optional 或类似类型的基础值“ wrap" 一个值并进行布尔转换以指示值的存在。例如:
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);
唯一的缺点是它必须小心使用,并且不会给你一个正确使用的静态检查。例如。您可以在没有?: fallback 的情况下执行coalesce(opt2),这与在不首先检查它是否包含任何内容的情况下执行*opt2 相同。所以coalesce 这个名字有点误导,但在我看来,如果使用得当,它看起来不言自明(而且非常整洁)。
【讨论】: