Search code examples
c++c++11rvalue-reference

std:forward inside a template class


 template<typename T>
   class BlockingQueue
   { 
       std::queue<T> container_;

       template< typename U >
       void push(U&& value)
       {
           static_assert(std::is_same<T, typename std::remove_reference<U>::type>::value,"Can't call push without the same parameter as template parameter's class");

           container_.push(std::forward<U>(value)); 
       }
};

I would like BlockingQueue::push method be able to handle both rvalue and lvalue reference of object of type T to forward it to std::queue::push correct version. Is it preferable to do like the above code, or to provide two versions of push method inside my BlockingQueue class ? One for lvalue and one for rvalue


Solution

  • The implementation seems correct to me and does the job.

    Nevertheless, providing different implementations for lvalues and rvalues might be a good idea in your case. The main reason (that I can think of) is that deduction of template type arguments doesn't work for braced-init-lists. Consider:

    struct foo {
        foo(std::initializer_list<int>) {
        }
    };
    
    // ...
    foo f{1, 2, 3};    // OK
    
    BlockingQueue<foo> b;
    

    With the OP's code (*)

    b.push(f);         // OK
    b.push({1, 2, 3}); // Error
    

    If instead, the following overloads of BlockingQueue::push are provided:

    void push(const T& value) {
        container_.push(value); 
    }
    
    void push(T&& value) {
        container_.push(std::move(value)); 
    }
    

    Then the line that used to fail will work fine.

    The same arguments apply to aggregates. For instance, if foo was defined as

    struct foo {
        int a, b, c;
    };
    

    one would observe the same behaviors described above.

    My conclusion is that, if you want BlockingQueue to support more types (including aggregates or types with constructors taking std::initializer_lists), then it's better to provide two different overloads.

    (*) A small correction in the OP's code: in the static_assert you need to use typename

    typename std::remove_reference<U>::type>::value
    ^^^^^^^^