热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

是否可以在C++中实现DefaultIfNull函数?

免责声明:这更多是出于好奇,而不是缺乏其他解决方案!是否可以在C++中实现一个函数:传递一个T类型的

免责声明:这更多是出于好奇,而不是缺乏其他解决方案!

是否可以在 C++ 中实现一个函数


  • 传递一个 T 类型的指针

  • 要么向 T 指向的对象返回一个类似引用的东西

  • 或者,如果指针为空,则将类似引用的事物返回到T() 具有某种合理生命周期的默认构造?

我们的第一次尝试是:

template
T& DefaultIfNullDangling(T* ptr) {
if (!ptr) {
return T(); // xxx warning C4172: returning address of local variable or temporary
} else {
return *ptr;
}
}

第二次尝试是这样完成的:

template
T& DefaultIfNull(T* ptr, T&& callSiteTemp = T()) {
if (!ptr) {
return callSiteTemp;
} else {
return *ptr;
}
}

这消除了警告并在某种程度上延长了临时的生命周期,但我认为它仍然很容易出错。


背景:

整个过程是由如下所示的访问模式触发的:

if (pThing) {
for (auto& subThing : pThing->subs1) {
// ...
if (subThing.pSubSub) {
for (auto& subSubThing : *(subThing.pSubSub)) {
// ...
}
}
}
}

可以“简化”为:

for (auto& subThing : DefaultIfNull(pThing).subs1) {
// ...
for (auto& subSubThing : DefaultIfNull(subThing.pSubSub)) {
// ...
}
}

回答

没有真正符合您要求的良好、惯用的 C++ 解决方案。

一种“EmptyIfNull”可以很好地工作的语言,可能是一种具有垃圾收集或引用计数对象的语言。因此,我们可以通过使用引用计数指针在 C++ 中实现类似的功能:

// never returns null, even if argument was null
std::shared_pr
EmptyIfNull(std::shared_pr ptr) {
return ptr
? ptr
: std::make_shared();
}


或者,您可以返回对具有静态存储持续时间的对象的引用。但是,在使用这种技术时,我不会返回可变引用,因为一个调用者可能会将对象修改为非空,这可能会使另一个调用者非常困惑:

const T&
EmptyIfNull(T* ptr) {
static T empty{};
return ptr
? *ptr
: empty;
}

或者,您仍然可以返回一个可变引用,但不修改空对象的文档是调用者必须遵守的要求。这会很脆弱,但对于 C++ 课程来说,这是标准的。


作为另一种选择,我写了一个使用类型擦除包装器的建议,它可以是引用或对象,但Ayxan Haqverdili已经涵盖了它。大量的样板文件。


一些对前提进行更多调整以适合 C++ 的替代设计:

返回一个对象:

T
EmptyIfNull(T* ptr) {
return ptr
? *ptr
: T{};
}

让调用者提供默认值:

T&
ValueOrDefault(T* ptr, T& default_) {
return ptr
? *ptr
: default_;
}

将非空参数视为前提条件:

T&
JustIndirectThrough(T* ptr) {
assert(ptr); // note that there may be better alternatives to the standard assert
return *ptr;
}

将空参数视为错误情况:

T&
JustIndirectThrough(T* ptr) {
if (!ptr) {
// note that there are alternative error handling mechanisms
throw std::invalid_argument(
"I can't deal with this :(");
}
return *ptr;
}



背景:

我认为您要求的功能对于您提供的背景不是很有吸引力。目前,如果指针为空,您什么都不做,而根据此建议,您将对空对象执行某些操作。如果您不喜欢深度嵌套的块,则可以使用以下替代方法:

if (!pThing)
continue; // or return, depending on context
for (auto& subThing : pThing->subs1) {
if (!subThing.pSubSub)
continue;
for (auto& subSubThing : *subThing.pSubSub) {
// ...
}
}

或者,也许您可​​以建立一个不变量,您永远不会在范围中存储 null,在这种情况下,您永远不需要检查 null。





回答

是的,但它会很丑:

#include
#include
template
struct Proxy {
private:
std::variant m_data = nullptr;
public:
Proxy(T* p) {
if (p)
m_data = p;
else
m_data = T{};
}
T* operator->() {
struct Visitor {
T* operator()(T* t) { return t; }
T* operator()(T& t) { return &t; }
};
return std::visit(Visitor{}, m_data);
}
};
struct Thing1 {
int pSubSub[3] = {};
auto begin() const { return pSubSub; }
auto end() const { return pSubSub + 3; }
};
struct Thing2 {
Thing1* subs1[3] = {};
auto begin() const { return subs1; }
auto end() const { return subs1 + 3; }
};
template
auto NullOrDefault(T* p) {
return Proxy(p);
}
int main() {
Thing1 a{1, 2, 3}, b{4, 5, 6};
Thing2 c{&a, nullptr, &b};
auto pThing = &c;
for (auto& subThing : NullOrDefault(pThing)->subs1) {
for (auto& subSubThing : NullOrDefault(subThing)->pSubSub) {
printf("%d, ", subSubThing);
}
putchar('n');
}
}






推荐阅读
author-avatar
琪琪
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有