I am playing around with the answer to this question and I am getting different results between clang and gcc. With the following code:
#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;
}
I get this in clang and VS:
0
3
3
3
and this in gcc:
3
3
3
3
In VS I can see that the destructor for vector<int>{1,2,3}
is being called after z
is created. So I guess my example above is undefined behavior. reversion_wrapper holds a reference to a destroyed r-value. So my questions are:
- Is my conclusion above correct? If not, why do the compilers generate different output? and why is clang zeroing out the size? Also, I would guess that w is also undefined behavior.
- What would be the proper way to construct a struct wrapper that accepts r-values and l-values, if possible keeping the constness of the object being wrapped?
edit 1
I can bind r-value variable to const& so I am wondering if something like this wouldn't work?
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;
};