-1

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:

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, none 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

What is the correct way to use std::forward?

halfer
  • 19,824
  • 17
  • 99
  • 186
Leon
  • 1,489
  • 1
  • 12
  • 31
  • I was unable to reproduce. After I cleaned up the code for the missing header files, and removed all the warning that the compiler displayed, and fixed all the errors, and added the missing code so the example would compile and run... well, it compiled and run. Perhaps a [mcve] so I can see what you did that I didn't do, or vice versa. – Eljay Apr 17 '23 at 16:43
  • 6
    I think std::forward( src_ ) is wrong, use std::forward( src_ ) – jls28 Apr 17 '23 at 16:45
  • Haha, my real codes are too much more. If paste all of them to here, it is hard to read, and those are not related. I'm coding a reproducable version that can be pasted here and easy to read. – Leon Apr 17 '23 at 16:46
  • 2
    **Always** use the forwarding reference type for the template parameter of `forward`: `std::forward( src_ )` -> `std::forward( src_ )` – NathanOliver Apr 17 '23 at 16:47
  • With a prvalue the overload `bool enque( T& src_ )` is not applicable, at least not unless `T` contains a `const` modifier. You need `bool enque(T&& src_)` (which is an rvalue reference, not a forwarding reference in this scenario). – fabian Apr 17 '23 at 16:50
  • Btw: `construct` has been removed from `std::allocator` in C++20... You should use [`std::allocator_traits::construct`](https://en.cppreference.com/w/cpp/memory/allocator_traits/construct). – fabian Apr 17 '23 at 16:59
  • In order not to do wrong forwarding, I always take the type argument from the runtime argument: `std::forward(x);` – Red.Wave Apr 17 '23 at 18:07

1 Answers1

6

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_traits::construct

Cosemuckel
  • 397
  • 1
  • 8