为什么 'std::tie' d 对象的结构化绑定失败?
Why structured binding fails with 'std::tie' d objects?
我很好奇this asked
问题如下:
#include <iostream>
#include <set>
#include <iterator>
#include <array>
#include <tuple>
#include <type_traits>
int main()
{
const std::set<int> s{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
auto iter = s.find(5);
using IterType = decltype(iter);
// using `std::array` works fine!
const auto& [pv1, nxt1] = std::array<IterType, 2>{std::prev(iter), std::next(iter)};
std::cout <<"using std::array<IterType, 2> :"<< *pv1 << " " << *nxt1 << '\n'; // prints: 4 6
// using ` std::make_tuple` works fine!
const auto& [pv2, nxt2] = std::make_tuple(std::prev(iter), std::next(iter));
std::cout << "using std::make_tuple :" << *pv2 << " " << *pv2 << '\n'; // prints: 4 6
// using `std::tie` deduction happens in MSVC, but not in GCC and Clang
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
// following is an assertion failure in MSVC with /O2 /std:c++17
std::cout << "using std::tie :" << *pv3 << " " << *nxt3<< '\n';
}
I std::tie
d std::prev
和 std::next
的返回迭代器,并允许
进行 auto
推导的结构化绑定。
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
看起来它允许的唯一编译器是 MSVC v19.14 和 /O2 /std:c++17
!
GCC 9.1 和 clang 8.0 不同意这一点。在在线编译器中看到:https://godbolt.org/z/DTb_OZ
海湾合作委员会说:
<source>:23:28: error: no matching function for call to 'tie'
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
^~~~~~~~
/opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/tuple:1605:5: note: candidate function [with _Elements = <std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>>] not viable: expects an l-value for 1st argument
tie(_Elements&... __args) noexcept
^
铿锵声说:
<source>: In function 'int main()':
<source>:23:46: error: cannot bind non-const lvalue reference of type 'std::_Rb_tree_const_iterator<int>&' to an rvalue of type 'std::_Rb_tree_const_iterator<int>'
23 | const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
| ~~~~~~~~~^~~~~~
In file included from <source>:5:
/opt/compiler-explorer/gcc-9.1.0/include/c++/9.1.0/tuple:1611:19: note: initializing argument 1 of 'constexpr std::tuple<_Elements& ...> std::tie(_Elements& ...) [with _Elements = {std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>}]'
1611 | tie(_Elements&... __args) noexcept
| ~~~~~~~~~~^~~~~~~~~~
查看cppreference.com中给出的示例
MSVC 是正确的吗?或者谁在这里,为什么?
有趣的是 运行
std::cout << "using std::tie :" << *pv3 << " " << *nxt3<< '\n';
给我
(在 MSVS 2019, /std:c++17
)
template<class... TTypes>
constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;
这里,参数是非常量左值引用。 std::prev(iter)
和 std::next(iter)
不能绑定到左值引用,因此代码应该被拒绝。 MSVC 接受这个的原因在 :
中解释
Enable /Za
flag to compiler, then the code will be refused. MVSC
[sic] has extenstion which allows to bind temps to Lvalue reference.
tie
gets Lvalue references, but prev
, next
returns temporary. –
rafix07 2019-07-25
07:52:58Z
我很好奇this asked 问题如下:
#include <iostream>
#include <set>
#include <iterator>
#include <array>
#include <tuple>
#include <type_traits>
int main()
{
const std::set<int> s{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
auto iter = s.find(5);
using IterType = decltype(iter);
// using `std::array` works fine!
const auto& [pv1, nxt1] = std::array<IterType, 2>{std::prev(iter), std::next(iter)};
std::cout <<"using std::array<IterType, 2> :"<< *pv1 << " " << *nxt1 << '\n'; // prints: 4 6
// using ` std::make_tuple` works fine!
const auto& [pv2, nxt2] = std::make_tuple(std::prev(iter), std::next(iter));
std::cout << "using std::make_tuple :" << *pv2 << " " << *pv2 << '\n'; // prints: 4 6
// using `std::tie` deduction happens in MSVC, but not in GCC and Clang
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
// following is an assertion failure in MSVC with /O2 /std:c++17
std::cout << "using std::tie :" << *pv3 << " " << *nxt3<< '\n';
}
I std::tie
d std::prev
和 std::next
的返回迭代器,并允许
进行 auto
推导的结构化绑定。
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
看起来它允许的唯一编译器是 MSVC v19.14 和 /O2 /std:c++17
!
GCC 9.1 和 clang 8.0 不同意这一点。在在线编译器中看到:https://godbolt.org/z/DTb_OZ
海湾合作委员会说:
<source>:23:28: error: no matching function for call to 'tie'
const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
^~~~~~~~
/opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/tuple:1605:5: note: candidate function [with _Elements = <std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>>] not viable: expects an l-value for 1st argument
tie(_Elements&... __args) noexcept
^
铿锵声说:
<source>: In function 'int main()':
<source>:23:46: error: cannot bind non-const lvalue reference of type 'std::_Rb_tree_const_iterator<int>&' to an rvalue of type 'std::_Rb_tree_const_iterator<int>'
23 | const auto& [pv3, nxt3] = std::tie(std::prev(iter), std::next(iter));
| ~~~~~~~~~^~~~~~
In file included from <source>:5:
/opt/compiler-explorer/gcc-9.1.0/include/c++/9.1.0/tuple:1611:19: note: initializing argument 1 of 'constexpr std::tuple<_Elements& ...> std::tie(_Elements& ...) [with _Elements = {std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>}]'
1611 | tie(_Elements&... __args) noexcept
| ~~~~~~~~~~^~~~~~~~~~
查看cppreference.com中给出的示例 MSVC 是正确的吗?或者谁在这里,为什么?
有趣的是 运行
std::cout << "using std::tie :" << *pv3 << " " << *nxt3<< '\n';
给我
(在 MSVS 2019, /std:c++17
)
template<class... TTypes> constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;
这里,参数是非常量左值引用。 std::prev(iter)
和 std::next(iter)
不能绑定到左值引用,因此代码应该被拒绝。 MSVC 接受这个的原因在
Enable
/Za
flag to compiler, then the code will be refused. MVSC [sic] has extenstion which allows to bind temps to Lvalue reference.tie
gets Lvalue references, butprev
,next
returns temporary. – rafix07 2019-07-25 07:52:58Z