非模板 std::reference_wrapper 赋值运算符和模板构造函数

Posted

技术标签:

【中文标题】非模板 std::reference_wrapper 赋值运算符和模板构造函数【英文标题】:non-template std::reference_wrapper assignment operator and template constructor 【发布时间】:2020-09-04 21:35:35 【问题描述】:

在 C++ 20 标准中,类模板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 );

并且编译器没有发出与赋值运算符相关的错误消息。

这是编译器的错误还是我遗漏了什么?

【问题讨论】:

【参考方案1】:

如果std::reference_wrapper&lt;T&gt; 有一个接受T&amp; 的构造函数,那么它会导致诸如std::reference_wrapper&lt;const T&gt; 能够绑定到T 类型的临时对象之类的错误。所以,原来有两个构造函数(除了拷贝构造函数):一个取T&amp;,另一个取T&amp;&amp;,定义为删除,保证会出现编译错误。

然而,有人指出这并不是我们真正想要的:如果std::reference_wrapper&lt;T&gt; 完全没有构造函数来接受T 类型的右值,而不是删除构造函数,那就更好了。这是LWG 2993。 (您会注意到cppreference page 底部提到了这个问题。)因此,构造函数已更改为适当禁用 SFINAE 的模板。

一旦为构造函数解决了这些问题,赋值运算符只需有一个采用reference_wrapper 的重载即可。当编译器形成隐式转换序列时,转换问题将由构造函数逻辑处理。

【讨论】:

但标准不允许构造函数接受 std::reference_wrapper 类型的对象。 @VladfromMoscow 有一个复制构造函数,不是吗? 复制构造函数也是非模板构造函数。 @VladfromMoscow 是的,有一个复制构造函数和一个模板转换构造函数,但我不明白你打算用这个去哪里。

以上是关于非模板 std::reference_wrapper 赋值运算符和模板构造函数的主要内容,如果未能解决你的问题,请参考以下文章

C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译

第3章 非类型模板参数:3.1 非类型类模板参数

为啥可变参数模板的模板特化与非可变模板的特化不同?

访问模板类的非模板库的静态数据

模板类中的非模板函数[关闭]

什么时候删除模板实例比删除非模板重载更可取?