Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

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?

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;
};
like image 859
Mac Avatar asked Oct 29 '22 00:10

Mac


1 Answers

auto z = reverse(vector<int>{1, 2, 3});

yes, using z.iterable is undefined behaviour due to zombie reference ( no temporary lifetime extension occurs here because there's no vector<> temporary nor a reference to bound to )

vector<int> c{ 1, 2, 3 };
auto w = reverse(move(c));

this is ok, move(c) simply casts c to vector<int>&&, w.iterable referers to c, but note that nothing is moved.

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?

regarding object lifetime, given a 'pure' wrapper ( that is, something holding references ) you can't. You always need to make sure no dangling references occur. Of course, you can always let your wrapper copy/move-construct rvalues , but this is seldom useful I'd say.

if the question is how you pass an argument preserving its non/const l/rvaluesness, it's called perfect forwarding. But, it's not what you want here because it makes little sense for your wrapper to store an rvalue reference.

so something like

template <typename T>
reversion_wrapper<std::remove_reference_t<T>> reverse( T&& iterable ) { return { iterable }; }

would do ( the remove_reference<> is not strictly necessary here, but it makes a more reasonable choice for the wrapper parameter ). Moreover, it's your choice if you want to disable rvalues altogether ( eg. if you expect your wrapper to be never used with temporaries ). In this case, you would either static_assert() inside reverse() or =delete for (const T&&) or use SFINAE to filter out the overload.

I can bind r-value variable to const& so I am wondering if something like this wouldn't work?

it's easier/cleaner to overload for T& and T const& in that case:

template <typename T>
reversion_wrapper<T> reverse( T& iterable ) { return { iterable }; }

template <typename T>
reversion_wrapper<const T> reverse( T const& iterable ) { return { iterable }; }

but I cannot see why you would want that.

like image 107
Massimiliano Janes Avatar answered Nov 15 '22 02:11

Massimiliano Janes