是否有C++等效的C#空合并运算符? 我在我的代码中进行了太多的空值检查。 因此,正在寻找一种减少空代码量的方法。
是否有C++等效的C#空合并运算符? 我在我的代码中进行了太多的空值检查。 因此,正在寻找一种减少空代码量的方法。
我刚刚发现了这个链接:??运算符,也称为空合并运算符
You also have it in C/C++ as a GNU extension using the
?:
operator :string pageTitle = getTitle() ?: "Default Title";
false ?: 123
是 123
,而 coalesce(false, 123)
将会是 false
。 - einpoklum在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;
吗? - UncleBensnullptr
。无论如何在C/C++中都没有null
,只有某些头文件中定义的NULL
。https://dev59.com/b3M_5IYBdhLWcg3wq1CF - Csaba TothA??B??C
的情况下,B??C
是第一个??
的右操作数。换句话说,它相当于A??(B??C)
。此外,没有限制右操作数不能为空,只是会导致整个表达式为空。 - Cemafor使用模板和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 内 - 我们无法避免这种样板。实际上,语言应该原生提供空值合并运算符。
??
和?.
运算符。这些宏确保:
COA( nullPtr, goodPtr )->sayHello();
COA( nullPtr, COA( nullPtr, goodPtr ) )->sayHello();
COACALL( goodPtr, sayHello() );
COACALL( nullPtr, sayHello() );
COACALL( COA( nullPtr, goodPtr ), sayHello() );
定义:
#define COA(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }())
#define COACALL(a, b) ([&](){ auto val = (a); if (val) (val->b); }());
COACALL
不返回结果。只能用于无返回值的调用或根据需要进行修改。我想要扩展@Samuel Garcia的答案,泛化模板并添加帮助器宏来减少lambda样板代码:
#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() );
?:
运算符,请参见Conditionals with Omitted Operands。
条件表达式中的中间操作数可以省略。如果第一个操作数非零,则其值为条件表达式的值。
因此,表达式
x ? : y
如果
x
非零,则其值为x
;否则,其值为y
。这个例子与
x ? x : y
等效。
在这种简单情况下,省略中间操作数的能力并不是特别有用。当第一个操作数包含副作用时(如果它是宏参数,则可能会包含副作用),重复中间操作数将执行两次副作用。省略中间操作数使用已经计算出的值,而不会重新计算它带来的不良影响。
这个扩展也被clang支持。但是,在使用扩展之前,您应该检查您正在使用的编译器和代码的可移植性要求。值得注意的是,MSVC C++编译器不支持?:
中省略的操作数。
另请参见相关的StackOverflow讨论here。
这个怎么样?
#define IFNULL(a,b) ((a) == null ? (b) : (a))
a
为空时才不执行b
),而函数的参数总是需要被计算的。 - Steve Jessopa
就不会被评估两次了。#define IFNULL(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }())
- Cory-G?:
操作符(也称为“Elvis操作符”)的答案:我有时会使用一个辅助函数和这个操作符一起,该函数获取指针或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
,这与在首次检查其是否包含任何内容之前执行* opt2
相同。因此,名称 coalesce
有点误导人,但在正确使用时,在我看来它看起来很自我解释(而且非常整洁)。??
,C#中的空值合并运算符的语义是:
p ?? q
Here,
p
is the left andq
is the right operand of [the]??
operator. The value ofp
can be nullable type, but the value of q must be non-nullable type. If the value ofp
isnull
, then it returns the value of q. Otherwise, it will return the value ofp
.
首先我们注意到 C++ 不能使用 ??
作为标识符;预处理器也不允许我们定义一个这个名称的宏。因此,我们可以使用 coalesce
作为标识符。
我们需要以请求的语义定义 coalesce
为中缀运算符。这可以通过运算符重载和宏的组合来实现,使我们可以编写:
int* p = // whatever
int* q = // whatever
int* result = p coalesce q;
这是一个实现:
#include <functional>
namespace detail {
struct coalesce_op {};
template <typename T>
struct coalesce_op_primed {
T&& lhs;
constexpr T&& operator+(T&& rhs) {
return (lhs == nullptr) ? rhs : lhs;
}
};
template <typename T>
constexpr coalesce_op_primed<T> operator+(T&& t, coalesce_op)
{
return coalesce_op_primed<T>{std::forward<T>(t)};
}
} // namespace detail
#define coalesce + detail::coalesce_op{} +
在GodBolt上看它的实际应用。
注意事项:
q
。可以编写具有短路逻辑的宏,但它不会成为中缀运算符。?:
Elvis运算符,因为其语义不同。它将考虑非空false值作为选择RHS操作数的原因,即false?:123
将产生123
,而false ?? 123
使用C#合并语义是false
。std::optional
,检测nullopt
。如果需要,也应该可以通过一些if-constexpr或tagged-dispatch TMP实现。
a ?: b
可以实现相同的功能,但这种写法不具备可移植性。 - MSaltersfalse ?: 123
是123
,而coalesce(false, 123)
是false
。 - einpoklumCOALESCE(NULL, 123)
是123
。我的非正式定义是“列表中第一个真实值”。 - MSalters