我正在玩this question的答案,我在clang和gcc之间得到了不同的结果。使用以下代码:
#include <iostream>
#include <vector>
using namespace std; // for rbegin() and rend()
template <typename T>
struct reversion_wrapper { T& iterable; };
template <typename T>
auto begin(reversion_wrapper<T> w) { return rbegin(w.iterable); }
template <typename T>
auto end(reversion_wrapper<T> w) { return rend(w.iterable); }
template <typename T>
reversion_wrapper<T> reverse(T&& iterable) { return { iterable }; }
int main() {
auto z = reverse(vector<int>{1, 2, 3});
cout << z.iterable.size() << '\n';
vector<int> a{ 1, 2, 3 };
auto x = reverse(a);
cout << x.iterable.size() << '\n';
const vector<int> b{ 1, 2, 3 };
auto y = reverse(b);
cout << y.iterable.size() << '\n';
vector<int> c{ 1, 2, 3 };
auto w = reverse(move(c));
cout << w.iterable.size() << '\n';
return 0;
}
我在clang和VS中得到了这个:
0
3
3
3
这是在gcc:
3
3
3
3
在VS中,我可以看到vector<int>{1,2,3}
的析构函数是在创建z
后调用的。所以我猜我上面的例子是未定义的行为。 reversion_wrapper保存对已销毁的r值的引用。所以我的问题是:
编辑1
我可以将r值变量绑定到const&amp;所以我想知道这样的事情是否会起作用?
template <typename T>
struct reversion_wrapper {
static bool const rvalue;
using U = typename std::conditional_t<std::is_rvalue_reference_v<T>, const remove_reference_t<T>&, T&>;
U iterable;
};
答案 0 :(得分:2)
auto z = reverse(vector<int>{1, 2, 3});
是的,使用z.iterable是由于僵尸引用而未定义的行为(此处不会发生临时生命周期扩展,因为没有向量&lt;&gt;临时也没有绑定的引用)
vector<int> c{ 1, 2, 3 };
auto w = reverse(move(c));
这没关系,移动(c)只是将c转换为vector<int>&&
,w.iterable referers转换为c,但请注意没有任何移动。
构造一个接受r值和l值的struct wrapper的正确方法是什么,如果可能的话,保持对象的常量被包裹?
关于对象的生命周期,给定一个'纯'包装器(即持有引用的东西),你不能。您始终需要确保没有悬挂引用。当然,你总是可以让你的包装复制 / move-construct rvalues,但这很少有用。
如果问题是如何传递保留其非/ const l / rvaluesness的参数,则称为完美转发。但是,这不是你想要的,因为你的包装器存储rvalue引用没什么意义。
类似
template <typename T>
reversion_wrapper<std::remove_reference_t<T>> reverse( T&& iterable ) { return { iterable }; }
会这样做(remove_reference&lt;&gt;在这里不是绝对必要的,但它为包装器参数提供了更合理的选择)。此外,如果您想完全禁用右值(例如,如果您希望您的包装器永远不会与临时使用一起使用),则可以选择它。在这种情况下,你可以在reverse()内部使用static_assert()或在(const T&amp;&amp;)中删除=或使用SFINAE来过滤掉过载。
对于T&amp; T来说,过载更容易/更清洁和T const&amp;在那种情况下:我可以将r值变量绑定到const&amp;所以我想知道这样的事情是否会起作用?
template <typename T>
reversion_wrapper<T> reverse( T& iterable ) { return { iterable }; }
template <typename T>
reversion_wrapper<const T> reverse( T const& iterable ) { return { iterable }; }
但我不明白为什么你会这么想。