jeudi 12 octobre 2017

What is the proper way of creating a "thin" struct wrapper for an object?

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:

  1. 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.
  2. 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?

Aucun commentaire:

Enregistrer un commentaire