当"taking address of temporary"错误妨碍干净的代码时
When "taking address of temporary" error gets in a way of clean code
我知道这个错误消息被多次讨论过,其背后的原因是众所周知的。这个问题是关于不同的事情 - 如何编码某种模式(如果不是因为这个错误,这将是微不足道的)。
有一个函数void foo(int const*, int const*, int const*)
,当相关数据不可用时,每个参数都会nullptr
。我使用了int
,但它可以是任何类型的(参数的数量也不是固定的)。
使用MSVC(或在过去 - 使用-fpermissive
),您可以编写这样的代码,并且很容易看:
int data_source::bar() { ... }
data_source* s1;
data_source* s2;
data_source* s3;
...
foo(
s1 ? &s1->bar() : nullptr,
s2 ? &s2->bar() : nullptr,
s3 ? &s3->bar() : nullptr
);
现在,对于现代 GCC,这无法编译,-fpermissive
似乎不再有效。
您将如何构建此代码以在避免错误的同时执行相同的操作?有没有办法告诉编译器"是的,我知道,但没关系"(类似于(void)x
强制转换以避免unused variable
警告)?
我想出了两个"解决方案"(不喜欢它们):
template<class T> T const* addr(T const& v) { return &v; }
甚至更危险:
template<class T> T* addr(T&& v) { return &v; }
这意味着更多的打字,更难阅读,而且通常很丑陋。
这个试图模仿编译器在幕后所做的事情,但不幸的是,导致太多的类型,一个额外的比较(每个参数)以及生成的asm中的其他低效率:
optional<int> v1 = [&]{ return s1 ? optional<int>(s1->bar()) : optional<int>(); }();
optional<int> v2 = [&]{ return s2 ? optional<int>(s2->bar()) : optional<int>(); }();
optional<int> v3 = [&]{ return s3 ? optional<int>(s3->bar()) : optional<int>(); }();
foo(
v1 ? &v1.value() : nullptr,
v2 ? &v2.value() : nullptr,
v3 ? &v3.value() : nullptr
);
问题不在于语言与干净的代码冲突。问题在于,您使用 C 形式的"可选"参数作为误导的微优化形式。C++17 为此引入了一种词汇类型,恰如其分地命名为std::optional
。我认为foo
最好看起来像这样:
void foo(std::optional<int>, std::optional<int>, std::optional<int>);
它具有干净的编码人员应该喜欢的自我记录功能。由于它也按值传递,因此无需在接口中人为地将其标记为const
。最后,调用不是问题:
auto extract_argument = [](data_source* s) {
return s ? optional<int>{s->bar()} : optional<int>{};
};
foo(
extract_argument(s1),
extract_argument(s2),
extract_argument(s2)
);
您标记了C++11,所以我不会触及上面可能会boost::optional
作为替身掉落的地方。但应该注意的是,在 C++17 中,它几乎可以与原始代码相同:
foo(
s1 ? std::optional{s1->bar()} : std::nullopt,
s2 ? std::optional{s2->bar()} : std::nullopt,
s3 ? std::optional{s3->bar()} : std::nullopt
);
指定std::optional
是必需的,因为我们希望条件表达式具有明确定义的类型,但可以使用std::nullopt
指定"null"大小写。更重要的是,类模板参数推导意味着我们不需要指定模板参数。所以它简短,富有表现力,切中要害。
解决方案#1 C++代码是正确的,显然是实现我的目标的唯一方法:
template<class T> T* addr(T&& v) { return &v; }
foo(
s1 ? addr(s1->bar()) : nullptr,
s2 ? addr(s2->bar()) : nullptr,
s3 ? addr(s3->bar()) : nullptr
);
进一步说明:由于C++14获取右值的地址不再是UB-现在只是禁止,编译器需要发出错误。
如果foo
尝试取消引用它作为参数获得的非 NULL 指针,则调用它
foo(
s1 ? &s1->bar() : nullptr,
s2 ? &s2->bar() : nullptr,
s3 ? &s3->bar() : nullptr
);
导致未定义的行为。如果你宁愿找到一种方法告诉编译器"我知道我在做什么。闭嘴,去做吧",你粗心大意,傲慢自大。
如果foo
没有取消引用非 NULL 指针,则可以使用:
int temp;
foo(
s1 ? &temp : nullptr,
s2 ? &temp : nullptr,
s3 ? &temp : nullptr
);
并避免调用data_source::bar()
.
无论哪种情况,都使用
foo(
s1 ? &s1->bar() : nullptr,
s2 ? &s2->bar() : nullptr,
s3 ? &s3->bar() : nullptr
);
完全没有保证。
- 将鼠标悬停在问题上时与预期">"相关的代码错误
- VI工作室代码错误无法打开输出文件主.exe
- 引入参数化构造函数后显示 LNK 2019 未解析外部符号的代码错误
- 有关矩阵的代码错误导致分段错误(内核转储)
- C++ Schannel POST 400 错误代码错误请求
- Java 本机访问代码错误:"Invalid memory access"
- 代码错误修改
- C++代码错误分配
- "_FCbuild"不能用作函数 - C/C++ 代码错误
- 我的代码错误类型警告 1 警告 C4018 和更多错误
- 代码错误(从 1 到 100 找到质数)
- OpenCV VisualStudio,C 代码错误尝试访问像素
- 当我使用的教程完全没有错误时,为什么我的代码错误
- 简单的NOOB C 输入/输出问题:代码错误
- 为什么enumProcessModules返回false值和299代码错误
- WinhttpSetoption()失败设置TLSV1.2,带有错误代码错误_internet_incorrect_ha
- 苹果安全传输代码错误
- C++ - 国际象棋主教移动代码错误
- 导入Python库时,嵌入了Python代码错误
- 编译提升C 代码错误