为什么编译器找不到赋值运算符?

huangapple go评论61阅读模式
英文:

Why can't the compiler find the assignment operator?

问题

template <typename T>
class MyPointer
{
public:
    template <typename U>
    void operator=(MyPointer<U>&& other)
    {
        // Your code here...
    }
    char* get() const { return pointer; }
private:
    char* pointer;
};

int main()
{
    struct B {};
    struct D : B{};
    
    MyPointer<B> my_pointer_b;
    MyPointer<D> my_pointer_d;
    
    my_pointer_b = my_pointer_d;
}

错误信息:

二进制 '=':找不到接受右操作数类型为 'MyPointermain::D' 的运算符(或没有可接受的转换)

编译器会为你使用的具体类型实例化赋值运算符,所以即使删除了默认赋值运算符,实例化的赋值运算符也应该存在。

英文:
template &lt;typename T&gt;
class MyPointer
{public:


    template &lt;typename U&gt;
    void operator=(MyPointer&lt;U&gt;&amp;&amp; other)
    {
      
    }
    char* get() const { return pointer; }
private:
    char* pointer;
};

int main()
{
    struct B {};
    struct D : B{};   

    MyPointer&lt;B&gt; my_pointer_b;
    MyPointer&lt;D&gt; my_pointer_d;

    my_pointer_b = my_pointer_d;


}

The error I get:

> binary '=': no operator found which takes a right-hand operand of type
> 'MyPointer<main::D>' (or there is no acceptable conversion)

The compiler instantiates the assignment operator for the particular type that I use, so even if it deleted the default one the instantiated one should be there.

答案1

得分: 6

或许与 gcc 出现的错误 有关,能更清楚地说明这个问题:

&lt;source&gt;:24:20: 错误:无法将类型为 &#39;MyPointer&lt;main()::D&gt;&amp;&amp;&#39; 的右值引用绑定到类型为 &#39;MyPointer&lt;main()::D&gt;&#39; 的左值
   24 |     my_pointer_b = my_pointer_d;
      |                    ^~~~~~~~~~~~

你的 MyPointer&lt;U&gt;&amp;&amp; 不是转发引用。它是右值引用。

来自 cppreference

> 转发引用是一种特殊类型的引用,它保留了函数参数的值类别,使得可以通过 std::forward 进行转发。转发引用可以是以下两种情况之一:
> 1) 函数模板的函数参数,声明为同一函数模板的模板参数的右值引用到非 cv 限定类型:
> 2) [... auto&& ...]

这是一个转发引用:

template &lt;typename T&gt; void foo(T&amp;&amp;);

这个不是:

template &lt;typename T&gt; void bar(X&lt;T&gt;&amp;&amp;);
英文:

Perhaps the error with gcc helps to shed more light on this:

&lt;source&gt;:24:20: error: cannot bind rvalue reference of type &#39;MyPointer&lt;main()::D&gt;&amp;&amp;&#39; to lvalue of type &#39;MyPointer&lt;main()::D&gt;&#39;
   24 |     my_pointer_b = my_pointer_d;
      |                    ^~~~~~~~~~~~

Your MyPointer&lt;U&gt;&amp;&amp; is not a forwarding reference. Its a rvalue reference.

From cppreference:

> Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward. Forwarding references are either:
> 1) function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template:
> 2) [... auto&& ...]

This is a forwarding reference

template &lt;typename T&gt; void foo(T&amp;&amp;);

This one is not

template &lt;typename T&gt; void bar(X&lt;T&gt;&amp;&amp;);

答案2

得分: 5

template <typename U>
void operator=(MyPointer<U>&& other)

在这里,MyPointer<U>&& 不是一个转发引用。它是对某个 MyPointer<U> 的右值引用,其中 U 是从参数中推导出来的。

因此,您不能使用左值调用这个函数。

如果您想要使用一个转发引用,但要将其限制为 MyPointer 实例,可以像这样做:

// 前向声明
template <typename T>
class MyPointer;

template <typename T>
struct IsMyPointerInstance : std::false_type {};

template <typename T>
struct IsMyPointerInstance<MyPointer<T>> : std::true_type {};

template <typename T>
concept MyPointerInstance = IsMyPointerInstance<std::remove_cvref_t<T>>::value;

template <typename T>
class MyPointer {
public:
    template <typename U> requires MyPointerInstance<U>
    void operator=(U&& other) {
        // ...
    }
}

演示

(或者如果您不能使用概念,可以使用 SFINAE)

英文:
template &lt;typename U&gt;
void operator=(MyPointer&lt;U&gt;&amp;&amp; other)

In this, MyPointer&lt;U&gt;&amp;&amp; is not a forwarding reference. It's an rvalue reference to some MyPointer&lt;U&gt;, where U is deduced from the argument.

As a result, you cannot call this function with an lvalue.

If you want to use a forwarding reference but constrain it to a MyPointer instance, then you can do something like this:

// forward declaration
template &lt;typename T&gt;
class MyPointer;

template &lt;typename T&gt;
struct IsMyPointerInstance : std::false_type {};

template &lt;typename T&gt;
struct IsMyPointerInstance&lt;MyPointer&lt;T&gt;&gt; : std::true_type {};

template &lt;typename T&gt;
concept MyPointerInstance = IsMyPointerInstance&lt;std::remove_cvref_t&lt;T&gt;&gt;::value;

template &lt;typename T&gt;
class MyPointer {
public:
    template &lt;typename U&gt; requires MyPointerInstance&lt;U&gt;
    void operator=(U&amp;&amp; other) {
        // ...
    }

Demo

(or use SFINAE if you can't use concepts)

huangapple
  • 本文由 发表于 2023年7月6日 20:54:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76629062.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定