4

Background:

I am trying to implement a function, let's call it mysort, that takes variable number of arguments of same type and sort them. For example the code below should print 1 2 3

template <typename ... Args>
void mysort(Args& ... args);
int main(int, char**)
{
    int x = 3;
    int y = 1;
    int z = 2;
    mysort(x, y, z);
    cout << x << ' ' << y << ' ' << z << endl;
    return 0;
}

What I have tried:

Here is the solution I came up with

#include <array>
#include <algorithm>
#include <functional>

template <typename T, typename ... Args>
void mysort(T& t, Args& ... args)
{
    constexpr size_t n = sizeof...(Args) + 1;
    std::array<std::reference_wrapper<T>, n> temp = { std::ref(t), std::ref(args)... };
    std::sort(temp.begin(), temp.end());
}

But this solution doesn't work because what std::swap does with std::reference_wrapper is not what I expect (I expect any changes to std::reference_wrapper should change the object for which std::reference_wrapper was created).

Everything starts working after adding

namespace std
{
    void swap(std::reference_wrapper<int>& a, std::reference_wrapper<int>& b) noexcept
    {
        std::swap(a.get(), b.get());
    }
}

I'm not sure if the standard requires std::sort to use std::swap, so I think this is not a complete solution and is implementation-dependent.

Summarizing:

My idea of a std::reference_wrapper is that it should behave just like a reference, but that doesn't seem to be the case. Here are my questions:

  1. What is the best way to implement mysort?
  2. Why doesn't the standard provide a std::swap specification for std::reference_wrapper that would swap underlying objects?

Thank you in advance for your answers!

4
  • 5
    Before std::swap, operator= already doesn't do what you think: it rebinds (on purpose). Commented Apr 23, 2023 at 17:06
  • 1
    temp will end up being a sorted list of references to your original variables en.cppreference.com/w/cpp/utility/functional/reference_wrapper/…. std::reference wrapper doesn't behave like a reference, it allows storing a reference where it wouldn't normally be possible to store one, to do that it needs to be copyable/assignable Commented Apr 23, 2023 at 17:08
  • My idea of a std::reference_wrapper is that it should behave just like a reference, but that doesn't seem to be the case. That is, indeed, not the case. Commented Apr 23, 2023 at 17:25
  • The actual use case wants something else: Usualy you use a container of reference wrappers because you cant have a contianer of references and then you want std::sort to shuffle the elements in the container not what they refer to Commented Apr 23, 2023 at 17:49

2 Answers 2

5

The point of std::reference_wrapper is that it is copyable and assignable, "assignable" in the sense that a reference wrapper can be rebound to a new value the way that a normal reference cannot.

You're doing the opposite of what it is intended for. In your case when the implementation of std::sort swaps items in the temporary array it is rebinding the references, but this is not what you want. You want it to swap the actual items not the references.

One way to do this would be to let std::sort sort copies of the arguments and then assign them back to your reference arguments in sorted order:

template <typename T, typename ... Args>
void mysort(T& t, Args& ... args)
{
    constexpr size_t n = sizeof...(Args) + 1;
    std::array<T, n> temp = { t, args... };
    std::sort(temp.begin(), temp.end());
    std::tie(t, args...) = std::tuple_cat(temp);
}

Here I wrap them in a tuple so I can assign via std::tie. There may be some way to do this with fewer copies, but this is what came to mind.

Sign up to request clarification or add additional context in comments.

Comments

-2

Basically, std::reference_wrapper is an old poorly designed class that currently serves certain simple purposes, like in std::bind/bind_front or in construction of std::thread, where it is used to imply that the variables aren't copied but taken by reference. And one creates them via std::ref/cref.

I just don't recommend ever using std::reference_wrapper aside from template cases where it is used to tell the difference whether user wants to forward a copy or a reference to the object.

If you want a reference imitating class that performs an assign/move-through rather than rebind that's up to you to implement. Just you should be aware that it can be problematic in certain situations.

Also don't ever write any kind of

namespace std
{
    void swap(std::reference_wrapper<int>& a, std::reference_wrapper<int>& b) noexcept
    {
        std::swap(a.get(), b.get());
    }
}

It might break a lot of code that you import.

12 Comments

In what way is std::reference_wrapper poorly designed? It does exactly what it needs to do and no more? Extra functionality would be wasteful in the use cases it was designed for
@AlanBirtles it was designed way before std::thread and std::bind existed. And it just failed to find any proper use. It was simply reused for these objects as it fitted their construction scheme.
Actually boost::bind, boost::thread and boost::ref were all released at the same time in version 1.25.0 boost.org/doc/libs/1_31_0. Even if it did predate them it doesn't mean it's poorly designed. You still haven't stated what's poor about it's design?
@AlanBirtles the class doesn't do anything useful, except for this specific purpose which makes it more of an internal service class rather than an API that should be exposed to users.
It fulfills that purpose exactly as needed though so seems perfectly designed? Maybe you actually mean the language is poorly designed making this class necessary?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.