昨天我在SO上看到了一个关于结构化绑定的有趣问题.
我们可以总结如下.请考虑以下示例代码:
#include <tuple>
#include <type_traits>
int main() {
auto tup = std::make_tuple(1, 2);
auto & [ a, b ] = tup;
// the following line won't compile for a isn't a reference
// static_assert(std::is_reference_v<decltype(a)>);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下decltype(a)是int(可能),因为这种子弹(工作草案):
if
e是一个未加括号的id-expression,用于命名结构化绑定[...],decltype(e)是结构化绑定声明规范中给出的引用类型
这是@Curious在评论中为感兴趣的人提供的wandbox片段.它表明实际上a不是参考,仅此而已.
到目前为止好于原来的问题,OP问为什么它int,而不是int &和标准说,看上去像一个可以接受的答案.
无论如何,我想知道委员会为何如此决定.在一天结束时,a引用元组中的元素,我可以通过修改该元素a.换句话说,声明a看起来像一个引用,它的行为类似于引用,但它不是引用.
我可以忍受这个,但我想知道背后的原因是什么.为什么decltype(a)不能简单int &?亵渎者可以理解是否有一个有意义的理由?
C++ 17标准引入了一种新的结构化绑定功能,该功能最初于2015年提出,其语法外观在后面被广泛讨论.
一看完文档就会想到它们的一些用途.
聚合分解
让我们宣布一个元组:
std::tuple<int, std::string> t(42, "foo");
Run Code Online (Sandbox Code Playgroud)
命名元素副本可以通过一行中的结构化绑定轻松获得:
auto [i, s] = t;
Run Code Online (Sandbox Code Playgroud)
这相当于:
auto i = std::get<0>(t);
auto s = std::get<1>(t);
Run Code Online (Sandbox Code Playgroud)
要么
int i;
std::string s;
std::tie(i, s) = t;
Run Code Online (Sandbox Code Playgroud)
也可以无痛地获得对元组元素的引用:
auto& [ir, sr] = t;
const auto& [icr, scr] = t;
Run Code Online (Sandbox Code Playgroud)
所以我们可以使用所有成员都是公共的数组或结构/类.
多个返回值
从上面可以直接获得从函数中获取多个返回值的便捷方法.
还有什么?
你能为结构化绑定提供一些其他的,可能不太明显的用例吗?他们还能如何提高C++代码的可读性甚至性能?
笔记
正如评论中提到的,结构化绑定的当前实现缺少一些功能.它们是非可变参数,并且它们的语法不允许显式跳过聚合成员.在这里可以找到关于可变性的讨论.
我一直在编写一组类来允许一个简单的类似python的zip函数.以下代码片段(几乎)可以正常工作.然而,这两个变量a并b没有const.
std::vector<double> v1{0.0, 1.1, 2.2, 3.3};
std::vector<int> v2{0, 1, 2};
for (auto const& [a, b] : zip(v1, v2))
{
std::cout << a << '\t' << b << std::endl;
a = 3; // I expected this to give a compiler error, but it does not
std::cout << a << '\t' << b << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我一直在使用gcc 7.3.0.这是MCVE:
#include <iostream>
#include <tuple>
#include <vector>
template <class ... Ts>
class zip_iterator
{
using value_iterator_type = …Run Code Online (Sandbox Code Playgroud) 当使用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没有发出任何警告感到惊讶.
以下代码段摘自cppref:
std::tuple<int, int&> f();
auto [x, y] = f();
// decltype(x) is int
// decltype(y) is int&
const auto [z, w] = f();
// decltype(z) is const int
// decltype(w) is int&
Run Code Online (Sandbox Code Playgroud)
我的问题是在最后一行:
为什么 是 decltype(w) int& 不是 const int&?
假设我们有一个名为AAA支持复制/移动的类:
class AAA
{
public:
AAA() = default;
~AAA() = default;
AAA(const AAA& rhs)
{
std::cout << "Copy constructor" << std::endl;
}
AAA(AAA&& rhs)
{
std::cout << "Move constructor" << std::endl;
}
};
Run Code Online (Sandbox Code Playgroud)
在以下代码中,get_val返回second:
AAA get_val()
{
auto [ first, second ] = std::make_tuple(AAA{}, AAA{});
std::cout << "Returning - " << std::endl;
return second;
}
auto obj = get_val();
std::cout << "Returned - " << std::endl;
Run Code Online (Sandbox Code Playgroud)
现在second被复制,打印以下输出:
...
Returning - …Run Code Online (Sandbox Code Playgroud) 强制复制省略是否适用于通过结构化绑定进行分解?以下哪种情况适用于?
// one
auto [one, two] = std::array<SomeClass>{SomeClass{1}, SomeClass{2}};
// two
auto [one, two] = std::make_tuple(SomeClass{1}, SomeClass{2});
// three
struct Something { SomeClass one, two; };
auto [one, two] = Something{};
Run Code Online (Sandbox Code Playgroud)
我怀疑只有第三种情况允许复制省略,因为前两个会被"分解"通过std::get<>并std::tuple_size<>和std::get<>当参数是右值返回xvalues
标准的引用也很好!
阅读C++ 17,现在可以在if语句中进行多次初始化:
if (int x = func(), y = func2(); x > 0 && y > 0)
{
}
Run Code Online (Sandbox Code Playgroud)
不错的,也结合了C++ 17中的另一个功能,结构化绑定:
if (auto[iter, success] = set.insert("Hello"); success)
{ }
else
{ }
Run Code Online (Sandbox Code Playgroud)
但是,在VisualStudio 2017中无法编译这两个功能.
if (auto[iter, success] = set.insert("Hello"), [iter2, success2] = set.insert("Foo"); success && success2)
{}
else
{}
Run Code Online (Sandbox Code Playgroud)
失踪 ';' 之前','
这是VS2017中的错误还是不可能?
我正在尝试编写一个模板函数,该函数使用已解析的ADL get来获取struct/range(tuple-esque)的成员.
#include <iostream>
#include <utility>
#include <tuple>
int main() {
auto tup = std::make_tuple(1, 2);
std::cout << get<0>(tup) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我这样做是因为结构化绑定提案(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf§11.5.3 )说的是如何get习惯的从结构中获取元素.它表示非成员get用于从结构中获取元素.
我假设上面的代码会编译,因为ADL会导致get在std命名空间中查找函数(因为它的参数是类型std::tuple<int, int>,在其中std),在那里可以找到它.但是,我收到了一个错误.有人可以在这里解释正确的方法,以及为什么上面的代码不起作用?在这种情况下如何强制ADL发生?
我知道我能做到
auto&& bla = something();
Run Code Online (Sandbox Code Playgroud)
并且根据const返回值的something不同,我会得到一个不同的类型bla.
这是否也适用于结构化绑定案例,例如
auto&& [bla, blabla] = something();
Run Code Online (Sandbox Code Playgroud)
我猜是这样的(结构化绑定捎带在auto初始化器上,表现得像这样),但我找不到肯定的肯定.
更新:初步测试似乎符合我的预期(const正确推导出来):
#include <tuple>
using thing = std::tuple<char, int*, short&, const double, const float&>;
int main()
{
char c = 0;
int i = 1;
short s = 2;
double d = 3.;
float f = 4.f;
thing t{c, &i, s, d, f};
auto&& [cc, ii, ss, dd, ff] = t;
c = 10;
*ii = 11; …Run Code Online (Sandbox Code Playgroud) c++ language-lawyer forwarding-reference c++17 structured-bindings