Zul*_*lan 19 c++ rvalue c++17 structured-bindings
当使用std::minmax结构化绑定时,我遇到了一个相当微妙的错误.似乎传递的rvalues并不总是像人们期望的那样被复制.最初我T operator[]() const在自定义容器上使用a ,但它似乎与文字整数相同.
#include <algorithm>
#include <cstdio>
#include <tuple>
int main()
{
auto [amin, amax] = std::minmax(3, 6);
printf("%d,%d\n", amin, amax); // undefined,undefined
int bmin, bmax;
std::tie(bmin, bmax) = std::minmax(3, 6);
printf("%d,%d\n", bmin, bmax); // 3,6
}
Run Code Online (Sandbox Code Playgroud)
使用GCC 8.1.1 -O1 -Wuninitialized将导致0,0打印为第一行,并且:
warning: ‘<anonymous>’ is used uninitialized in this function [-Wuninitialized]
Run Code Online (Sandbox Code Playgroud)
Clang 6.0.1 at -O2也会在没有警告的情况下给出错误的第一个结果.
在-O0GCC给出正确的结果而没有警告.对于clang,结果似乎是正确的-O1或-O0.
在rvalue仍然有效被复制的意义上,第一行和第二行不应该是等价的吗?
另外,为什么这取决于优化级别?特别是我对GCC没有发出任何警告感到惊讶.
Sto*_*ica 11
什么是需要注意的auto [amin, amax]是,auto,auto&等是在由物体施加e一个与返回值进行初始化std::minmax,这是一对.它基本上是这样的:
auto e = std::minmax(3, 6);
auto&& amin = std::get<0>(e);
auto&& amax = std::get<1>(e);
Run Code Online (Sandbox Code Playgroud)
实际的类型amin和amax是引用该对象的任何std::get<0>和std::get<1>返回的引用.并且他们自己返回对已经过去的对象的引用!
使用时std::tie,您正在分配现有对象(通过引用传递).rvalues不需要比它们产生的赋值表达式更长寿.
作为一种解决方法,你可以使用这样的东西(不是生产质量)功能:
template<typename T1, typename T2>
auto as_value(std::pair<T1, T2> in) {
using U1 = std::decay_t<T1>;
using U2 = std::decay_t<T2>;
return std::pair<U1, U2>(in);
}
Run Code Online (Sandbox Code Playgroud)
它确保该对保持值类型.当像这样使用时:
auto [amin, amax] = as_value(std::minmax(3, 6));
Run Code Online (Sandbox Code Playgroud)
我们现在得到一份副本,结构化绑定指的是那些副本.
这里有两个基本问题:
min,max并且minmax由于历史原因返回参考.因此,如果你传入一个临时的,你最好按值取结果或立即使用它,否则你得到一个悬空参考.如果minmax给你一个pair<int, int>而不是一个pair<int const&, int const&>,你就不会有任何问题.auto衰减顶级cv -qualifiers并剥离引用,但它不会完全删除.在这里,你推断出这一点pair<int const&, int const&>,但如果我们推断出来pair<int, int>,我们再也不会有任何问题.(1)比(2)更容易解决:编写自己的函数以按值获取所有内容:
template <typename T>
std::pair<T, T> minmax(T a, T b) {
return (b < a) ? std::pair(b, a) : std::pair(a, b);
}
auto [amin, amax] = minmax(3, 6); // no problems
Run Code Online (Sandbox Code Playgroud)
通过价值获取所有东西的好处是你永远不必担心隐藏的悬挂引用,因为没有任何.并且这些函数的绝大多数用法无论如何都使用整数类型,因此引用没有任何好处.
当你确实需要引用时,当你比较昂贵的复制对象时...好吧,更容易采取一个带值的函数并强制它使用引用而不是采用一个使用引用和试着解决它:
auto [lo, hi] = minmax(std::ref(big1), std::ref(big2));
Run Code Online (Sandbox Code Playgroud)
此外,在呼叫网站上我们使用引用非常明显,所以如果我们搞砸了会更加明显.
虽然上面因为reference_wrapper<T>隐式转换而适用于许多类型T&,但它不适用于那些具有非成员,非朋友,运算符模板(如std::string)的类型.不幸的是,你还需要为参考包装器编写专门化.
| 归档时间: |
|
| 查看次数: |
725 次 |
| 最近记录: |