我正在玩弄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;
}
我得到这个铛和VS:
0
3
3
3
,这在GCC:
3
3
3
3
在VS,我可以看到,对于vector<int>{1,2,3}
的析构函数在创建z
后调用。所以我想我上面的例子是未定义的行为。 reversion_wrapper保存对被销毁的r值的引用。所以我的问题是:
- 我的结论是否正确?如果不是,为什么编译器会生成不同的输出?为什么铿锵声大小?另外,我猜测w也是未定义的行为。
- 构建一个接受r值和l值的结构包装的正确方法是什么?如果可能,保持对象的常量被包装?
编辑1
我可以绑定R值变量为const &所以我想,如果这样的事情是行不通的?
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;
};
有趣的问题。我在标准的某个地方见过,那个被const引用引用的临时对象只要引用它就可以存活。这里的问题是1)你的引用被包装在另一个对象中,2)你通过一个函数调用并且3)这个标准可能意味着在定义点直接实现。你的推理看起来是正确的,但我们仍然可以长时间讨论标准的含义。 – Tomek