非模板 std::reference_wrapper 赋值运算符和模板构造函数
non-template std::reference_wrapper assignment operator and template constructor
在 C++ 20 标准中,class 模板的构造函数 std::reference_wrapper
是一个模板。
template<class U>
constexpr reference_wrapper(U&&) noexcept(see below );
而赋值运算符不是模板
constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
这些特殊成员函数之间存在这种差异(模板和非模板)的原因是什么?
另一方面,我使用 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 );
}
并且编译器没有发出与赋值运算符相关的错误消息。
这是编译器的错误还是我遗漏了什么?
如果 std::reference_wrapper<T>
有一个接受 T&
的构造函数,那么它会导致 std::reference_wrapper<const T>
能够绑定到 T
类型的临时对象等错误。所以,本来有两个构造函数(复制构造函数除外):一个取T&
,另一个取T&&
,定义为删除,确保编译错误。
然而,有人指出这并不是我们真正想要的:如果 std::reference_wrapper<T>
根本没有接受类型 T
的右值的构造函数会更好,相反到已删除的构造函数。这是 LWG 2993. (You'll notice that this issue is mentioned at the bottom of the cppreference page。)因此,构造函数已更改为适当禁用 SFINAE 的模板。
一旦为构造函数解决了这些问题,赋值运算符只需要有一个重载reference_wrapper
。当编译器形成隐式转换序列时,转换问题将由构造函数逻辑处理。
在 C++ 20 标准中,class 模板的构造函数 std::reference_wrapper
是一个模板。
template<class U>
constexpr reference_wrapper(U&&) noexcept(see below );
而赋值运算符不是模板
constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
这些特殊成员函数之间存在这种差异(模板和非模板)的原因是什么?
另一方面,我使用 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 );
}
并且编译器没有发出与赋值运算符相关的错误消息。
这是编译器的错误还是我遗漏了什么?
如果 std::reference_wrapper<T>
有一个接受 T&
的构造函数,那么它会导致 std::reference_wrapper<const T>
能够绑定到 T
类型的临时对象等错误。所以,本来有两个构造函数(复制构造函数除外):一个取T&
,另一个取T&&
,定义为删除,确保编译错误。
然而,有人指出这并不是我们真正想要的:如果 std::reference_wrapper<T>
根本没有接受类型 T
的右值的构造函数会更好,相反到已删除的构造函数。这是 LWG 2993. (You'll notice that this issue is mentioned at the bottom of the cppreference page。)因此,构造函数已更改为适当禁用 SFINAE 的模板。
一旦为构造函数解决了这些问题,赋值运算符只需要有一个重载reference_wrapper
。当编译器形成隐式转换序列时,转换问题将由构造函数逻辑处理。