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

How to perfectly forward a universal reference that is either a const ref or a movable rvalue?

I have coded a lock-free and thread-safe ring queue with C++20, and it works so far.
The only thing is not perfect that it has to have two enque() methods, one accepts a const reference to a lvalue as a argument, and the other accpects a reference to a rvalue, so as to move a rvalue into the queue instead of constructing again.

The before version of codes is following and merely a skeleton, to be simplified:

template <typename T>
class RingQue_t {
public:
    explicit RingQue_t( size_t capacity );
    ~RingQue_t();
    bool deque( T& dest_ ) { return true; };

    // If we offer a const ref, the compiler will select this one
    bool enque( const T& src_ ) {
        // a lot of same codes goes here for a same logic...

        new( _buff + _tail ) T( src_ );
    };

    // If we offer a rvalue ref, the compiler will select this one
    bool enque( T& src_ ) {
        // a lot of same codes goes here for a same logic...

        new( _buff + _tail ) T( std::move( src_ ) );
    };

protected:
    T*  _buff = nullptr;
};

I’m trying to merge these two methods into one, and had read some docs and examples about std::forward, but I still can NOT use it correctly.
This is my expecting:

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>
class RingQue_t {
public:
    template<typename U>
    bool enque( U&& src_ ) {
        // new( _buff + _tail ) T( src_ );
        // new( _buff + _tail ) T( std::move( src_ ) );
        // new( _buff + _tail ) T( std::forward<T>( src_ ) );

        std::allocator<T> allc;
        allc.construct( _buff + _tail, std::forward<T>( src_ ) );
        return true;
    };
};

// testing
const std::string s0 { "s0" };
RingQue_t<std::string> str_que( 16 );
str_que.enque( std::string { "s1" } ); // works
str_que.enque( s0 ); // can not pass the compiling.

All solutions in comments had been tried, noone works.
I always receive a error msg:

binding reference of type ‘std::remove_referencestd::__cxx11::basic_string<char >::type&’ {aka ‘std::__cxx11::basic_string&’} to ‘const std::__cxx11::basic_string’ discards qualifiers

Pls tell me the correct way to use std::forward, thanks a lot!!!

>Solution :

The proplem is related to the the fact, that enque() does not properly forward the constness of the argument. This is because U is deduced as const T&, but after forwarding using std::forward<T>() this constness is lost. In order to fix this simply replace std::forward<T>() with std::forward<U>()

Also note that std::allocator<T>::construct is deprecated in c++17, instead use std::allocator<T>::construct_at

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