具有rvalue-reference成员的通用拷贝构造函数

时间:2013-09-26 23:46:30

标签: c++ c++11 rvalue-reference

我正在研究一个简单的包装器模板类,它在调用特殊成员函数时记录。这些函数不能默认,因为它们执行其他与日志记录相关的任务。

template <typename T>
struct logger {
    logger(T const& value) : value_(value) { /*...log...*/ }
    logger(T&& value) : value_(std::move(value)) { /*...log...*/ }
    logger(logger const& other) : value_(other.value_) { /*...log...*/ }
    logger(logger&& other) : value_(std::move(other.value_)) { /*...log...*/ }

    T value_;
};

不幸的是,当包装类型是 rvalue-reference 时, copy-constructor 无法使用以下错误消息进行编译:

  

错误:无法将'int'左值绑定到'int&amp;&amp;'

原因是对于 rvalue-reference 成员,隐式复制构造函数的行为会有所不同:

  

[class.copy 12.8 / 15]   非联合类X的隐式定义的复制/移动构造函数执行其基础和成员的成员复制/移动。 [...] x成为构造函数的参数,或者对于移动构造函数,使用引用参数的xvalue。以适合其类型的方式复制/移动每个基本或非静态数据成员:

     
      
  • 如果成员是一个数组,则每个元素都使用x的相应子对象进行直接初始化;
  •   
  • 如果成员m的右值引用类型为T&&,则会使用static_cast<T&&>(x.m)进行直接初始化;
  •   
  • 否则,基础或成员使用x的相应基础或成员进行直接初始化。
  •   

这让我想到了一个问题:即使在使用时,如何编写一个泛型复制构造函数,其作用是一个隐式定义的复制构造函数 rvalue-references 作为成员。

对于这种特殊情况,我可以为 rvalue-references 添加一个额外的特化。但是,我正在寻找一种不限于单个成员的通用解决方案,并且不会引入代码重复。

2 个答案:

答案 0 :(得分:3)

这里有龙。

logger(logger const& other) : value_(other.value_)

表达式other.value_T const类型的左值,例如int&int&&int const

  1. 如果T == int&&,您需要执行move,因为表达式是左值。 move相当于static_cast<int&&>,因此您也可以直接执行static_cast

  2. 如果T == int&,则不需要演员表。

  3. 如果T == int,则不需要演员表。

  4. 对于定义为:

    的副本
    logger(logger const& other) : value_(static_cast<T>(other.value_)) {/*...*/}
    

    应用于第三种情况,这被定义为引入临时,并可能导致额外的复制/移动,但我认为它可以&amp;将被省略。

    不依赖于复制/移动省略的解决方案是引入weird_cast,在任何情况下都会生成所需的类型:

    #include <type_traits>
    
    template<class T, class U>
    typename std::enable_if<std::is_reference<T>{}, T>::type
    weird_cast(U& p)
    {
        return static_cast<T>(p);
    }
    
    template<class T, class U>
    typename std::enable_if<not std::is_reference<T>{}, T const&>::type
    weird_cast(U const& p)
    {
        return p;
    }
    
    int main()
    {
        int           o = 42;
        int &        lo = o;
        int &&       ro = std::move(o);
        int const   lco = o;
    
        int&& r = weird_cast<int&&>(ro);
        int&  l = weird_cast<int& >(lo);
        int   d = weird_cast<int  >(lco);
    }
    

    这类似于std::forward,但也支持“转发”非引用类型。


    龙在哪里?

    [class.copy] / 11指定:

      

    如果XX,则将{{1}}类的默认复制/移动构造函数定义为已删除:

         
        
    • [...]
    •   
    • 表示复制构造函数,rvalue引用类型
    • 的非静态数据成员   
    • [...]
    •   

    右值引用通常绑定到xvalue或prvalue,即指向一个表达“接近其生命周期结束”的对象的表达式。由于生命周期不会通过函数边界扩展,因此容易出现这样的“复制”。

答案 1 :(得分:0)

您可以为rvalue-references写一个专门化:

template<typename T>
struct logger<T&&>{
  ...
};

但实际上我认为你不希望logger::_value成为右值参考...

修改

虽然我认为这不是一个糟糕的解决方案,因为它是所有右值引用的GENERAL解决方法,这里是另一个没有文字特化的选项:

template<typename TT>
struct logger{
  typedef typename rvalue_remover<TT>::value T;
  //your previous code here
};

其中rvalue_remover是这样的:

template<typename T>struct rvalue_remover{typedef T value;};
template<typename T>struct rvalue_remover<T&&>{typedef T value;};

我很确定这已经在c ++ 11中定义了,但我没有在这里安装它所以我不记得这个名字了。

<强> EDIT2

啊!找到了!它被称为std::remove_reference<T>::type,并在#include <type_traits>

中声明