【问题标题】:No matching constructor for initialization of MyClassNameMyClassName 的初始化没有匹配的构造函数
【发布时间】:2020-11-17 23:54:08
【问题描述】:

我有一个课程,在代码中我粘贴了我认为问题所在的部分。

class SubtreeExplorer : public AbstractTask {
 public:
  SubtreeExplorer(Threadpool& tp, SudokuBoard&& sudoku)
      : tp(tp), sudoku(std::move(sudoku)) {}

 ...

 private:
  Threadpool& tp;
  SudokuBoard sudoku;
 
  bool sudoku_backtracking_search(SudokuBoard& s) {
   ...
   while(...){
      ...
      // let another thread explore the subtree
      tp.submit(make_shared<SubtreeExplorer>(tp, SudokuBoard(sudoku)));
   }
  }
};

当我尝试编译它时,它给出了错误:

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:4325:5: error: static_assert failed due to requirement 'is_constructible<SubtreeExplorer,
      Threadpool &, SudokuBoard &>::value' "Can't construct object in make_shared"
    static_assert( is_constructible<_Tp, _Args...>::value, "Can't construct object in make_shared" );

我正在使用 VS Code,在 IDE 中没有突出显示错误。 问题可能是由make_shared 引起的,但我无法弄清楚导致它的SubtreeExplorer 类的构造函数中是什么。

Sudokuboard(sudoku) 从给定的板返回一个新板,几乎没有修改。我希望将这个新板移动到新的子树资源管理器中。

在谷歌上看,我写的构造函数应该把板子移进去,但我仍然得到我写的编译错误,加上一长串难以阅读的笔记:

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:2259:9: note: in instantiation of function template specialization
      'std::__1::__compressed_pair_elem<SubtreeExplorer, 1, false>::__compressed_pair_elem<Threadpool &, SudokuBoard &, 0, 1>' requested here
        _Base2(__pc, _VSTD::move(__second_args),
        ^ /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:3672:16: note: in instantiation of function template specialization
      'std::__1::__compressed_pair<std::__1::allocator<SubtreeExplorer>, SubtreeExplorer>::__compressed_pair<std::__1::allocator<SubtreeExplorer> &,
      Threadpool &, SudokuBoard &>' requested here
            :  __data_(piecewise_construct, _VSTD::forward_as_tuple(__a),
               ^ /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:4331:26: note: in instantiation of function template specialization
      'std::__1::__shared_ptr_emplace<SubtreeExplorer, std::__1::allocator<SubtreeExplorer>
>::__shared_ptr_emplace<Threadpool &, SudokuBoard &>' requested
      here
    ::new(__hold2.get()) _CntrlBlk(__a2, _VSTD::forward<_Args>(__args)...);
                         ^ /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:4710:29: note: in instantiation of function template specialization
      'std::__1::shared_ptr<SubtreeExplorer>::make_shared<Threadpool &, SudokuBoard &>' requested here
    return shared_ptr<_Tp>::make_shared(_VSTD::forward<_Args>(__args)...);
                            ^ src/sudoku_parallel.cpp:119:13: note: in instantiation of function template specialization 'std::__1::make_shared<SubtreeExplorer, Threadpool &, SudokuBoard
      &>' requested here   tp.submit(make_shared<SubtreeExplorer>(tp, sudoku));
            ^ src/sudoku_parallel.cpp:41:3: note: candidate constructor not viable: no known conversion from 'SudokuBoard' to 'SudokuBoard &&' for 2nd argument   SubtreeExplorer(Threadpool& tp, SudokuBoard&& sudoku)   ^ src/sudoku_parallel.cpp:39:7: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided class SubtreeExplorer : public AbstractTask {
      ^ src/sudoku_parallel.cpp:39:7: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided

这让我觉得我向构造函数传递了错误数量的参数,即使它需要两个参数并且我传递了两个参数,一个线程池和一个数独板。

基本上,SubtreeExplorer 的创建类似于 sudoku_backtracking_search 函数的主体:

int main() {
   ...
   SubokuBoard sudoku(filepath);
   tp.submit(make_shared<SubtreeExplorer>(tp, sudoku));
   ...
}

编辑: tp 是一个线程池,它的提交方法有这个签名:

bool submit(std::shared_ptr<AbstractTask> task);

AbstractTask 类如下:

struct AbstractTask {
  virtual ~AbstractTask() = default;
  virtual void run() = 0;
};

【问题讨论】:

  • 使用构造函数的代码在哪里?请发minimal reproducible example
  • @cigien 不是tp.submit(make_shared&lt;SubtreeExplorer&gt;(tp, SudokuBoard(sudoku)));
  • 哦,好的,但是submit 等是什么?如果我们可以简单地复制您显示的代码并重现错误,那就太好了。
  • @cigien 我知道,但这是一个分成多个文件的代码。所以我必须粘贴所有的头文件和实现文件。我不认为这是可行的。我将编辑并插入提交的签名。
  • @idclev463035818 这是标准库实现的代码。

标签: c++ constructor c++17 shared-ptr move-semantics


【解决方案1】:

构造函数接受一个对SudokuBoard 的右值引用,但你将它传递给一个左值。这就是您收到错误的原因;左值引用不能隐式转换为右值引用,因此调用失败。

您可以使用以下任一选项来解决问题:

  1. 最好的选择是将SubtreeExplorer构造函数更改为按值接受第二个参数,这允许调用者移动构造或复制构造它,具体取决于他们是否需要保留参数的副本.您可以在需要时获得移动语义的好处,但副本仍然可以接受。

    (这里假设SudokuBoard 类型有一个复制构造函数。)

      SubtreeExplorer(Threadpool& tp, SudokuBoard sudoku)
          : tp(tp), sudoku(std::move(sudoku)) {}
    
  2. 通过应用std::move() 将右值引用传递给命名变量:

    SubokuBoard sudoku(filepath);
    tp.submit(make_shared<SubtreeExplorer>(tp, std::move(sudoku)));
    
  3. 完全删除命名变量并传递一个临时变量:

    tp.submit(make_shared<SubtreeExplorer>(tp, SudokuBoard{filepath}));
    

我建议实施选项 1,但是如果您在此 make_shared 调用之后不需要使用 sudoku 变量,那么实施选项 2 或 3 -- 否则你会做一个毫无意义的副本。

【讨论】:

  • 这正是我想要的。我选择了选项 1,它就像一个魅力。只是一个问题,我怎么知道一个对象什么时候会被复制构造或移动构造?
  • 我希望对象总是在里面移动,并且永远不会重复。
  • @ninazzo 如果您总是希望它被移动,那么您需要像以前一样使用右值引用。任何时候你不移动值并尝试复制它,你都会得到一个编译时错误,就像你在这里所做的那样。在这种情况下,解决方案是应用选项 2 或 3。可选地,您可以使用选项 1 以及 2 或 3,并使类 SudokuBoard 不可复制——尽管我想知道为什么需要强制执行那个。
  • 我的意思是,在我现在想做的用例中,我希望它总是被移动。他说第一个构造函数允许调用者移动构造或复制构造它。我是 C++ 新手。我什么时候复制它,什么时候移动它?
  • @ninazzo 你不会总是知道的。从技术上讲,默认值是复制(但是当编译器知道不会改变代码行为时(例如当变量在没有进一步访问的情况下被丢弃时),编译器可能会在内部对其进行优化以成为移动)。然而,当你有一个带有 && 的参数时,这会强制移动。如果你明确使用 std::move,你也知道它会被移动。顺便说一句,过分关注这一点很快就会导致臭名昭著的“过早优化”。仅当您知道数据集很大时才注意确保移动。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 2018-11-14
相关资源
最近更新 更多