当使用 operator=() 时使用 copy-ctor 的 C++ - 这究竟是如何工作的?

Posted

技术标签:

【中文标题】当使用 operator=() 时使用 copy-ctor 的 C++ - 这究竟是如何工作的?【英文标题】:C++ using copy-ctor when operator=() is used - exactly how does this work? 【发布时间】:2014-04-12 02:08:41 【问题描述】:

C++ 将 operator=() 赋值转换为构造的具体规则是什么?例如Foo foo = bar 实际上会调用 Foo 的构造函数,接受 bar 作为参数,如果它存在的话。我用谷歌搜索了它的工作原理,但似乎找不到任何东西。

我无法弄清楚为什么下面的赋值尝试采用构造函数但没有采用明显正确的构造函数:HandlePtr(TYPE&resource)。使用实际构造语法的构造可以正常工作,但不能使用赋值运算符。

代码(显然为简洁而编辑):

template< typename TYPE >
class HandlePtr 
public:
    HandlePtr( void ) = default;
    HandlePtr( HandlePtr< TYPE >& other ) = default;
    HandlePtr( TYPE& resource )  // generally I would make this explicit, but for testing purposes I took it out
    ~HandlePtr( void ) = default;

public:
    HandlePtr<TYPE>& operator=( TYPE& resource )  return *this; 
    HandlePtr<TYPE>& operator=( HandlePtr<TYPE>& other )  return *this; 
;

int main ( void ) 
    int x = 5;
    HandlePtr< int > g( x ); // works
    HandlePtr< int > i;i = x; // works
    HandlePtr< int > h = x; // doesn't work

            // also tried this just out of curiosity:
    HandlePtr< int > h = HandlePtr< int >( x ); // also does not work

    return 0;

错误:

shit.cpp: In function ‘int main()’:
try.cpp:19:24: error: no matching function for call to ‘HandlePtr<int>::HandlePtr(HandlePtr<int>)’
   HandlePtr< int > h = x; // doesn't work
                        ^
try.cpp:19:24: note: candidates are:
try.cpp:7:3: note: HandlePtr<TYPE>::HandlePtr(TYPE&) [with TYPE = int]
   HandlePtr( TYPE& resource )  // generally I would make this explicit, but for testing purposes I took it out
   ^
try.cpp:7:3: note:   no known conversion for argument 1 from ‘HandlePtr<int>’ to ‘int&’
try.cpp:6:3: note: HandlePtr<TYPE>::HandlePtr(HandlePtr<TYPE>&) [with TYPE = int]
   HandlePtr( HandlePtr< TYPE >& other ) = default;
   ^
try.cpp:6:3: note:   no known conversion for argument 1 from ‘HandlePtr<int>’ to ‘HandlePtr<int>&’
try.cpp:5:3: note: HandlePtr<TYPE>::HandlePtr() [with TYPE = int]
   HandlePtr( void ) = default;
   ^
try.cpp:5:3: note:   candidate expects 0 arguments, 1 provided
try.cpp:20:20: error: redeclaration of ‘HandlePtr<int> h’
   HandlePtr< int > h = HandlePtr< int >( x ); // also does not work
                    ^
try.cpp:19:20: error: ‘HandlePtr<int> h’ previously declared here
   HandlePtr< int > h = x; // doesn't work

【问题讨论】:

这不是operator=,它只是表示复制构造的语法。无论如何,您的构造函数不可能在这种情况下工作,因为您不能将非常量引用绑定到临时对象。这就是复制构造函数应该通过 const 引用来获取参数的原因之一。 即使让 ctor 采用 const 引用也不会让它采用那个。 问题已在Error: Conversion to non-scalar type回答。 @zeroth,好点,你的构造函数首先接受int 需要一个临时的非常量引用。 【参考方案1】:

您在声明中忽略了这一点:

T t = u;

这不是赋值运算符。 t = u; 不是声明的子表达式。这里唯一的表达式是u;并且计算表达式 u 的结果用作正在声明的对象 t 的初始化程序。

如果u 具有T 类型,则t 是从u 复制构造的。

如果u没有T类型,那么u首先需要转换为T类型。这将创建一个T 类型的右值

您没有任何接受右值的构造函数,因此T t = u; 和相同的T t = T(u); 都失败了。但是,T t(u) 成功,因为没有创建右值;值u 用作构造函数T(U &amp;) 的参数。

简化代码示例:

struct T

    T(int &);
    T(T&);
    T();
    T &operator=(int &);
;

int main()

    int x = 5;
    T g(x);   // OK, T(int &)
    T g2(5);   // fail, looks for T(int const &)
    T i;      // OK, T()
    i = x;    // OK, T::operator=(int&)
    T h3 = i; // OK, T(T&)
    T h1 = T(x);    // fail, looks for T(T const &)
    T h2 = x;       // fail, identical to previous line 

通常您应该使用const &amp; 作为复制构造函数和赋值运算符的参数;那么所有这些“失败”的情况都会变成“OK”,因为右值可以绑定到 const 引用。

【讨论】:

嗯,这就解释了。我故意没有让复制 ctor 使用 const 引用,因为由于类的预期设计,它必须通过非 const 引用复制成员。

以上是关于当使用 operator=() 时使用 copy-ctor 的 C++ - 这究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

根据 operator= 实现复制构造函数

当一个对象同时提供 `operator!` 和 `operator bool` 时,这在表达式 `!obj` 中使用?

Kotlin常用的 Kotlin 类 ① ( 嵌套类 | 数据类 | 数据类 copy 函数 | 数据类解构声明 operator fun component1 | 数据类运算符重载 )

Kotlin常用的 Kotlin 类 ① ( 嵌套类 | 数据类 | 数据类 copy 函数 | 数据类解构声明 operator fun component1 | 数据类运算符重载 )

Copy constructors, Assignment operators, and Copy assignment operator

在`dctor、copy ctor和copy assignment operator`中,为啥删除一个而让另一个隐式定义最有可能导致错误