Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

I want to move semantics but I get a universal reference which hides copy semantics

How can I deal with universal reference, when I want either to copy semantics with a function parameter const T& or move semantics with function parameter T&&. The later hides the first.

A sample code with algebraic vector operators follow.

#include <array>
#include <iostream>


template<typename T>
void neg(T &a) { for (auto &i : a) i = -i; }

// object 'a' remains available
template<typename T>
auto operator-(const T &a) { std::cout << "1\r\n"; T b = a; neg(b); return b; }

// object 'a' terminates its life
template<typename T>
auto operator-(T &&a) { std::cout << "2\r\n"; neg(a); return std::move(a); }

// make an rvalue
template<typename T1, typename T2>
auto operator+(const T1 &a, const T2 &b) { return a; }


int main()
{
    std::array<int, 4> a;
    auto d = -(a+a);        // outputs 2 : correct
    auto e = -a;            // outputs 2 : I want 1
    auto f = -std::move(a); // outputs 2 : correct
    return 0;
}

EDIT: One of the solutions proposed by user17732522 (please upvote him).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

template<typename T>
auto &&operator-(T &&a)
{
    std::cout << "2\r\n"; neg(a); return a;
}
template<typename T>
auto operator-(T &a)
{
    std::cout << "1\r\n";
    std::remove_const_t<std::remove_reference_t<T>> b = a;
    neg(b); return b;
}

int main()
{
    std::array<int, 4> a;
    auto d = -(a+a);        // now outputs 2 : correct
    auto e = -a;            // now outputs 1 : correct
    auto f = -std::move(a); // now outputs 2 : correct
    const std::array<int, 4> b{1,2,3,4};
    d = -(b+b);     // now outputs 2 : correct
    e = -b;         // now outputs 1 : correct
    return 0;
}

>Solution :

You just need to remove const from const T&.

With using U = std::array<int, 4>;:

The issue here is that T&& deduces T to U, not const U, since a in main isn’t const. And binding to U& instead of const U& is considered better in overload resolution.

If you remove const from const T&, then both candidates will after deduction have a function parameter U& and consequently neither will be better based on that.

However, in partial ordering of function templates the T& template will win over T&& and so the former will be chosen if the function argument is a lvalue.

This does however have the consequence that a in the function will not be const qualified. You can obtain a const reference from it by applying std::as_const.

Alternatively you can use a requires clause or std::enable_if to constrain the T&& template on T being a non-reference type. Or you can use a single function template and decide in its body how to operate based on the type of the reference with if constexpr. Relevant type traits: std::is_lvalue_reference_v<T> or std::is_reference_v<T> and std::is_lvalue_reference_v<decltype(a)>.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading