当前位置: 首页 > 知识库问答 >
问题:

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

哈扬
2023-03-14

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

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为构造函数的参数,或者,对于移动构造函数,为引用该参数的x值。以适合其类型的方式复制/移动每个基本或非静态数据成员:

  • 如果成员是数组,则每个元素都直接初始化为x的相应子对象
  • 如果成员m具有右值引用类型T

这就引出了我的问题:如何编写一个行为类似于隐式定义的复制构造函数的泛型复制构造函数,即使在使用右值引用作为成员时也是如此。

对于这种特殊情况,我可以为右值引用添加额外的专门化。然而,我正在寻找一种通用的解决方案,它不局限于单个成员,也不引入代码重复。


共有2个答案

尹俊雅
2023-03-14

您可以为右值引用编写专门化:

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\u remover如下所示:

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

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

编辑2

啊!!找到了!它被称为std::remove\u reference

薄烨
2023-03-14

这里有龙。

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

表达式other.value_< /code>是类型T const的左值,例如int

>

  • 如果T==int

    如果T==int

    如果T==int,则不需要强制转换。

    对于定义为:

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

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

    一个不依赖复制/移动省略的解决方案是引入一个奇怪的\u 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类的默认复制/移动构造函数具有以下特性,则该构造函数被定义为已删除:

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

    rvalue引用通常绑定到xvalue或prvalue,即绑定到引用“接近其生命周期结束”的对象的表达式。由于生命周期不会通过函数边界得到扩展,所以允许这样的“复制”很容易出错。

  •  类似资料:
    • 我试图在静态编程语言中创建一个非常简单的泛型NDArray类,它将lambda表达式作为初始化函数。 典型的用法是: 我的问题是Kotlin编译器抱怨构造函数中值的初始化 通过说“不能使用‘T’作为具体化类型参数。改用类”。为什么? 编辑: 如果我用我自己的MyArray替换静态编程语言Array实现,它会编译: 不确定为什么静态编程语言对待MyArray与常规数组不同,当两者具有相同的构造函数时

    • 问题内容: 如果我有一个像这样的抽象类: 还有一些从Item派生的类是这样的: 我不明白为什么我不能使用泛型调用构造函数: 我知道可以有一个没有构造函数的类型,但是这种情况是不可能的,因为Pencil具有没有参数的构造函数,而Item是抽象的。但是我从eclipse中得到了这个错误: 无法实例化 我不明白为什么的 T类型 ,以及如何避免这种情况? 问题答案: 无法使用Java类型系统来强制类层次结

    • 假设我有以下课程: 我想添加一个构造函数,它需要一个

    • 问题内容: 如果不可变的类对象副本将与原始副本相等,那么为什么Java 中的类具有副本构造函数?这是一个错误还是背后有原因?在Java文档中,指定了: 问题答案: 复制字符串的主要原因是为了 “修剪行李” ,即仅将底层char数组修剪为必需的字符。 基本的char数组可能太大,因为通过调用创建字符串时,char数组可以在新的字符串实例和源字符串实例之间共享;偏移量指向第一个字符,并且包括长度。 我

    • 问题内容: 众所周知,您可以使用类型参数在Java中创建一个通用类: 但是,您也可以使用泛型 构造函数 ,这意味着显式接收其自己的泛型类型参数的构造函数,例如: 我正在努力了解用例。这个功能让我做什么? 问题答案: 这个功能让我做什么? 它至少可以让您完成 三 件事,而这 三 件事是您无法做到的: 表达参数类型之间的关系,例如: } <撤回> 正如@Lino首先观察到的那样,它使您可以表达参数必须

    • 我有一个类,它有一个类。 在我的类方法中,我按如下方式实例化类: 编译器将此代码转换为: 使用反射显示类具有以下合成构造函数: 由于类是,编译器向其添加构造函数,因此没有人可以实例化该类。但是显然,类应该能够实例化它,因此编译器添加了另一个包私有构造函数,该构造函数反过来调用私有构造函数。另外,由于包私有构造函数的名称中有,所以普通Java代码不能调用它。 问题:为什么要合成一个私有构造函数和一个