C++:当父类具有唯一指针时,隐式转换不起作用

Pat*_*ick 1 c++

(我还不明白根本原因,所以标题可能不正确。)

\n

我有以下无法编译的 C++ 代码:

\n
#include <iostream>\n#include <memory>\n#include <vector>\n#include <variant>\n#include <optional>\n\ntemplate <typename T>\nclass A {\n public:\n  ~A() = default;\n  std::unique_ptr<int> ptr = nullptr;\n};\n\ntemplate <typename T>\nclass B : public A<std::vector<T>> {\n public:\n  static B<T> Create() {\n    return B<T>();\n  }\n};\n\nusing AllTypes = std::variant<B<int64_t>, B<uint64_t>, B<double>>;\n\nstd::optional<AllTypes> Get() {\n  // Error! No viable conversion!\n  return B<int64_t>::Create();\n}\n
Run Code Online (Sandbox Code Playgroud)\n

错误信息是:

\n
main.cpp: In function \xe2\x80\x98std::optional<std::variant<B<long int>, B<long unsigned int>, B<double> > > Get()\xe2\x80\x99:\nmain.cpp:34:28: error: could not convert \xe2\x80\x98B::Create() [with T = long int]()\xe2\x80\x99 from \xe2\x80\x98B\xe2\x80\x99 to \xe2\x80\x98std::optional, B, B > >\xe2\x80\x99\n   34 |   return B<int64_t>::Create();\n      |          ~~~~~~~~~~~~~~~~~~^~\n      |                            |\n      |                            B<long int>\n
Run Code Online (Sandbox Code Playgroud)\n

可以在此处重现该错误: https: //onlinegdb.com/2H-K9x0kq

\n

但如果我删除唯一的 ptr 变量ptr,它可以正确编译。如果我将 ptr 成员移动到子类,B它也会编译。

\n

任何人都可以给出一些关于问题所在的提示吗?现在我不知道要搜索什么关键字/规则。非常感谢!

\n

更新:

\n
    \n
  1. 我将原来的absl::StatusOr 更改为std::Optional。我觉得这些错误可能是由于同样的问题造成的。
  2. \n
\n

JaM*_*MiT 5

让我首先简化您的示例。我猜想已经有了显着的简化,但是还有更多的层可以被取消。特别是,B这是不必要的,就像将 包装variant在另一个模板中一样。

#include <memory>
#include <variant>

class A {
 public:
  virtual ~A() = default; // will be used as a base class
  std::unique_ptr<int> ptr = nullptr;
};

std::variant<A, int> Get() {
  // Error! No viable conversion!
  return A();
}
Run Code Online (Sandbox Code Playgroud)

正如您所报告的,如果unique_ptr删除它,这将起作用。这告诉我什么?的特性之一uniqur_ptr就是不可复制;有时此属性会引发构造错误。

另一个难题是声明析构函数会抑制移动构造函数。最终结果是您的类A既不能被复制(因为unique_ptr)也不能被移动(因为用户声明的析构函数)。因此,呈现这种情况的另一种方式如下:

#include <variant>

class A {
 public:
  A() = default;        // <-- must be explicitly declared now
  A(const A&) = delete; // <-- Was deleted because of the `unique_ptr`
  A(A&&) = delete;      // <-- Was deleted because of the user-declared destructor
};

std::variant<A, int> Get() {
  // Error! No viable conversion!
  // Can neither copy nor move this temporary object into a `variant`.
  return A();
}
Run Code Online (Sandbox Code Playgroud)

一种修复方法是删除用户声明的析构函数。然而,由于涉及到继承,这不是一个好的选择。另一个修复是声明移动构造函数(以及移动赋值,如果需要的话)。

#include <memory>
#include <variant>

class A {
 public:
  virtual ~A() = default; // will be used as a base class
  A() = default;
  A(A&&) = default; // <-- Allow move construction

  std::unique_ptr<int> ptr = nullptr;
};

std::variant<A, int> Get() {
  // No error!
  return A();
}
Run Code Online (Sandbox Code Playgroud)