【问题标题】:Why isn't this templated move constructor being called?为什么不调用这个模板化的移动构造函数?
【发布时间】:2022-07-06 12:31:59
【问题描述】:

我遇到了这种奇怪的情况:

template <typename pointed_t>
class MyPointer
{public:
    MyPointer() : pPointed(nullptr) {}
    /* PREVENT COMPILER-GENERATED FUNCTIONS */
    MyPointer(const MyPointer&);
    MyPointer(MyPointer&&);
    MyPointer& operator=(const MyPointer&);
    MyPointer& operator=(MyPointer&&);

    //----------
    pointed_t* pPointed;


    /* COPY CONSTRUCTOR */
    template <typename AnyPointerType>
    MyPointer(AnyPointerType& other)
    {
        
    }
    /* MOVE CONSTRUCTOR */
    template <typename AnyPointerType>
    MyPointer(AnyPointerType&& other)
    {
        

    }

};

int main()
{
    MyPointer<char> p1;

    MyPointer<char> p2 = p1; // COPY CONSTRUCTOR CALLED FINE
    MyPointer<char> p3 = std::move(p1); // RAISES BELOW LINKER ERROR
    /* Unresolved external symbol 
    public: __cdecl MyPointer<char>::MyPointer<char>(class MyPointer<char> &&)" */

}

所以 p2 = p1;调用模板复制构造函数很好,但 p3 = std::move(p1);不能调用模板化的移动构造函数?

所以结果是复制构造函数工作,但移动构造函数没有。除非它是不同的类型:

MyPointer<float> p1;

MyPointer<char> p2 = std::move(p1); // NOW THE TEMPLATED MOVE CONSTRUCTOR IS CALLED FINE

有人可以帮我理解为什么没有调用模板化移动构造函数吗?

【问题讨论】:

  • MyPointer(MyPointer&amp;&amp;); 定义在哪里?
  • 模板永远不是复制/移动构造函数。可能是骗人的:stackoverflow.com/questions/55845896/…
  • @Eljay 不是故意定义的,我不希望编译器调用它。我不希望它自动生成,我不希望它被调用,我希望我的模板被调用。
  • 通过声明移动构造函数,它参与了重载解析。由于它没有定义,它会导致链接器错误。 (鉴于 NathanOliver 的评论,您想要采取的方法可能是不可能的。)
  • @Eljay 但是为什么复制构造函数不会失败?这也没有定义。

标签: c++ templates move-semantics


【解决方案1】:

问题是从构造函数模板生成的构造函数永远不是复制/移动构造函数。

为什么复制构造函数不会失败?

因为您提供的用户声明的副本 ctor MyPointer::MyPointer(const MyPointer&amp;) 具有 const MyPointer&amp; 类型的参数,但您提供的相应模板化构造函数具有 AnyPointerType&amp; 类型的参数。请注意,后者没有低级const。所以当你写的时候:

MyPointer<char> p2 = p1; //this will use the template constructor that has no low-level const. 

在上面,模板版本选择非模板移动 ctor,因为模板版本有一个非常量参数,p1 也是非常量。您可以通过设置p1 const 来确认是这种情况,在这种情况下,您将收到相同的链接器错误,但这次是复制ctor。 Demo


所以结果是复制构造函数起作用了,但是移动构造函数不起作用。

MyPointer&lt;char&gt; p3 = std::move(p1); 不起作用,因为在这种情况下,您提供的用户声明的移动构造函数 MyPointer(MyPointer&amp;&amp;) 具有 MyPointer&amp;&amp; 类型的参数,并且相应的模板化 ctor 也具有参数 MyPointer&amp;&amp;。所以写的时候:

MyPointer<char> p3 = std::move(p1); //the compiler prefers the non-template user-delcared move ctor

用户声明的非模板移动 ctor MyPointer::MyPointer(MyPointer&amp;&amp;)优先于模板版本,但由于用户声明的 ctor 没有实现,我们会收到上述链接器错误。


解决方案

我想要调用我的模板。

在这种特殊情况下,有一个解决方案,如下所示。特别是,我们可以在用户声明的move ctor MyPointer::MyPointer(MyPointer&amp;&amp;)的参数中添加一个低级的const

template <typename pointed_t>
class MyPointer
{public:
    MyPointer() : pPointed(nullptr) {}
    /* PREVENT COMPILER-GENERATED FUNCTIONS */
    MyPointer(const MyPointer&);
//------------vvvvv------------------>const added here
    MyPointer(const MyPointer&&);
    MyPointer& operator=(const MyPointer&);
    MyPointer& operator=(MyPointer&&);

    //----------
    pointed_t* pPointed;


    /* COPY CONSTRUCTOR */
    template <typename AnyPointerType>
    MyPointer(AnyPointerType& other)
    {
        std::cout<<"templated copy"<<std::endl;
    }
    /* MOVE CONSTRUCTOR */
    template <typename AnyPointerType>
    MyPointer(AnyPointerType&& other)
    {
        
       std::cout<<"template move"<<std::endl;
    }

};

int main()
{

    MyPointer<char> p1;

    MyPointer<char> p2 = p1; //calls the templated version
    MyPointer<char> p3 = std::move(p1); //calls the templated version
}

Working demo

【讨论】:

  • 这仍然会留下一个问题,如果我尝试使用 const 值调用复制构造函数,它会给复制构造函数的链接器错误,对吧?
  • @Zebrafish 是的,这就是为什么我说“在这种特殊情况下有一个解决方案”。一般来说,虽然没有办法使用模板版本。我已经解释了这样做的原因,即从构造函数模板生成的构造函数永远不是复制/移动 ctor。
  • Ehh,我将为复制/移动构造函数赋值运算符重写函数,然后它们将采用自己的类。顺便说一句,这就是 std::shared_ptr 使用模板赋值运算符和构造函数所做的事情。我想知道它是如何解决这个问题的。
  • 好的,我检查了标准库文档,std::shared_pointer 定义了模板化和非模板化构造函数。
  • @Zebrafish 是的,std::shared_ptr constructors
猜你喜欢
  • 2012-04-29
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-04
  • 2010-11-06
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多