Search code examples
c++templatesassignment-operatorc++20reference-wrapper

non-template std::reference_wrapper assignment operator and template constructor


In the C++ 20 Standard the constructor of the class template std::reference_wrapper is a template.

template<class U>
constexpr reference_wrapper(U&&) noexcept(see below ); 

while the assignment operator is not a template

constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;

What is the reason that this difference (template and non-template) between these special member functions exists?

On the other hand, I tried the following progarm using Visual C++ 2019.

#include <iostream>
#include <functional>

struct A
{
    void f() const { std::cout << "A::f()\n"; }
    virtual void g() const { std::cout << "A::g()\n"; }
};

struct B : A
{
    void f() const { std::cout << "B::f()\n"; }
    void g() const override { std::cout << "B::g()\n"; }
};

int main()
{
    B b;

    std::reference_wrapper<A> r( b );

    r.get().f();
    r.get().g();


    r = std::reference_wrapper<B>( b );
}

and the compiler did not issue an error message relative to the assignment operator.

Is it a bug of the compiler or have I missed something?


Solution

  • If std::reference_wrapper<T> had a single constructor accepting T&, then it would lead to bugs like std::reference_wrapper<const T> being able to bind to temporaries of type T. So, originally there were two constructors (other than the copy constructor): one taking T&, and another taking T&&, which was defined as deleted, ensuring that a compile error would occur.

    However, it was pointed out that this is not really what we want: it would be better if std::reference_wrapper<T> would have no constructor at all that accepts an rvalue of type T, as opposed to a deleted constructor. This is LWG 2993. (You'll notice that this issue is mentioned at the bottom of the cppreference page.) Thus, the constructor was changed to a template that is SFINAE-disabled as appropriate.

    Once these issues are solved for the constructor, the assignment operator only needs to have a single overload taking reference_wrapper. The conversion issues will be handled by the constructor logic when the compiler forms the implicit conversion sequence.