我正在玩弄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