繁体   English   中英

具有右值引用成员的泛型复制构造函数

[英]Generic copy-constructor with rvalue-reference members

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

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_;
};

不幸的是,当包装的类型是右值引用时复制构造函数无法编译并显示以下错误消息:

错误:无法将“ int”左值绑定到“ int &&”

原因是隐式副本构造函数对于右值引用成员的行为有所不同:

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

  • 如果成员是数组,则用x的相应子对象直接初始化每个元素;
  • 如果成员m具有右值引用类型T&& ,则使用static_cast<T&&>(xm)直接初始化它;
  • 否则,将使用x的相应基数或成员直接初始化基数或成员。

这让我想到了一个问题:即使使用右值引用作为成员,如何编写一个表现为隐式定义的复制构造函数的通用复制构造函数

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

这是龙。

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 ,则不需要强制转换。

对于定义为:

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

适用于第三种情况,这被定义为引入临时文件,并且可能导致额外的复制/移动,尽管我认为可以并将会删除。

一个不依赖于复制/移动weird_cast的解决方案是引入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指定:

如果X具有以下条件,则将默认的类X复制/移动构造函数定义为已删除:

  • [...]
  • 对于副本构造函数,右值引用类型的非静态数据成员
  • [...]

右值引用通常绑定到x值或pr值,即绑定到引用“在其生命周期即将结束时”的对象的表达式。 由于生存期不会通过函数边界扩展,因此允许进行这种“复制”很容易出错。

您可以为rvalue-references编写专门化的代码:

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

但实际上我不认为您希望logger::_value成为右值引用...

编辑

尽管我觉得这不是一个不好的解决方案,因为它是所有右值引用的通用解决方法,但这是没有字面专门化的另一种选择:

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>声明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM