提升::变体; std::unique_ptr 和复制

Posted

技术标签:

【中文标题】提升::变体; std::unique_ptr 和复制【英文标题】:boost::variant; std::unique_ptr and copy 【发布时间】:2013-03-28 18:43:42 【问题描述】:

此问题确定不可复制类型不能与 Boost Variant 一起使用

Tree

template <class T = int>

class Tree

private:

         class TreeNode

         public:
                 std::unique_ptr Nodes
                 Move constructors and move assignment + other public members

         private:

                 TreeNode(const TreeNode &other);      (= delete not supported on compiler)
                 TreeNode& operator=(const TreeNode &rhs);    (= delete not supported on compiler)


         ;  // End Tree Node Class Definition


         Tree(const Tree &other);     (= delete not supported on compiler)
         Tree& operator=(const Tree &rhs);    (= delete not supported on compiler)

public:

         Move constructors and move assignment + other public members
;

TreeVisitor

class TreeVisitor : public boost::static_visitor<bool> 
public:
        TreeVisitor() 

        bool operator() (BinarySearchTree<std::string>& tree) const 
            return searchTree.load(tree);
        
private:

;

TreeVariant

typedef boost::variant<Tree<std::string>, Tree<int>> TreeVariant;     
TreeVariant tree;

Tree<std::string> stringTree;
Tree<int> intTree;

申请Visitors如下

tree = intSearchTree;
boost::apply_visitor(TreeVisitor(), tree)

还使用 boost::bind 来获取所需的参数

boost::bind(TreeVisitor(), tree, val, keyIndex);

类型的编译器错误

error C2248: 'Tree<T>::Tree' : cannot access private member declared in class 'Tree<T>'  <----- related to private copy constructor in Tree (not TreeNode)
tree = stringTree;  <-------  error related to assignment

Tree 编译正确并经过测试。如何解决这些编译错误,这些错误似乎与尝试获取 Tree 类的副本有关,而由于 std::unique_ptr,这是不可能的?

SSCCE

<class T = int>

class Tree

private:

class TreeNode

public:

    TreeNode() 
    ~TreeNode()   

    TreeNode(TreeNode &&other) : 
        key(other.key), index(other.index), left(std::move(other.left)), right(std::move(other.right)) 
    
        key = index = left = right = nullptr; 
    

    TreeNode &operator=(BTreeNode &&rhs)
     
        if(this != &rhs) 
         
            key = rhs.key; index = rhs.index; 
            left = std::move(rhs.left); right = std::move(rhs.right); 
            rhs.key = rhs.index = rhs.left = rhs.right = nullptr;
         
        return *this;
    

    TreeNode(const T &new_key, const T &new_index) :
        key(new_key), index(new_index), left(nullptr), right(nullptr) 

    friend class Tree;

private:

    TreeNode(const BinarySearchTreeNode &other);
    TreeNode& operator=(const BinarySearchTreeNode &rhs);

    std::unique_ptr<TreeNode> left;
    std::unique_ptr<TreeNode> right;

;  // End Tree Node Class Definition

std::unique_ptr<TreeNode> root;

BinarySearchTree(const BinarySearchTree &other);
BinarySearchTree& operator=(const BinarySearchTree &rhs);


public:

Tree() : root(nullptr), flag(false), run(true), leftCount(0), rightCount(0) 

~Tree() 

Tree(BinarySearchTree &&other) : root(std::move(other.root))  other.root = nullptr; 

Tree &operator=(BinarySearchTree &&rhs) 
 
    if(this != &rhs)
     
        root = std::move(rhs.root); 
        rhs.root = nullptr;
     
    return *this;



;

使用示例:

bool delete_()

    while(!instances.empty())
                    // grab first instance
                    keyIndex = instances.at(0);
                    // compute end of the tuple to delete
                    endIndex = keyIndex + sizeToDelete;

                    // read the first attribute
                    try
                        temp = boost::trim_copy(dataFile->readData(keyIndex, domainSize));
                    
                    catch (std::exception &e)
                        printw("Error reading from the data file");
                    

                    // delete tuple from data file
                    if(!dataFile->deleteTuple(keyIndex, endIndex))
                        printw("Error attempting to remove tuple");
                        if (writer_ != nullptr)
                            writer_ << "Error attempting to remove tuple";
                        try
                            printw("%s");
                            // close catalog and search file

                        
                        catch (std::exception &e)
                            e.what();
                        
                        // close data file
                        dataFile->closeFile();
                        return false;
                    


                    try
                        int val = boost::lexical_cast<int>(temp);

                        searchTree = intSearchTree;

                        boost::bind(BinarySearchTreeVisitor(), searchTree, val, keyIndex);

                        // delete key index from the index file
                        if (!boost::apply_visitor(BinarySearchTreeVisitor(), searchTree))
                            printw("No index present in index file");
                            try
                                printw(" ");

                            
                            catch (std::exception &e)

                            
                            // close data file
                            dataFile->closeFile();
                            return false;           
                        
                    
                    catch(boost::bad_lexical_cast &e)

                        /*
                         * Must be a std::string --- wow who knew
                         */

                        searchTree = stringSearchTree;

                        boost::bind(BinarySearchTreeVisitor(), searchTree, temp, keyIndex);

                        // delete key index from the index file
                        if (!boost::apply_visitor(BinarySearchTreeVisitor(), searchTree))
                            printw("No index present in index file");
                            try
                                printw(" ");
                                // close catalog and search file

                            
                            catch (std::exception &e)
                                e.what();
                            
                            // close data file
                            dataFile->closeFile();
                            return false;           
                        

                                           

                    // clean up the index file
                    boost::bind(BinarySearchTreeVisitor(), searchTree, keyIndex, sizeToDelete);
                    boost::apply_visitor(BinarySearchTreeVisitor(), searchTree);

                    instances.erase(instances.begin());

                    for(int i= 0; i < instances.size(); i++)
                        instances.assign(i, instances.at(i) - 
                                                            sizeToDelete);
                    

                

【问题讨论】:

请发布可编译的代码。 (并且仍然演示问题)删除不重要但仍然演示问题的内容。看这里:sscce.org 了解如何让您的问题更容易回答。 @Yakk 我的原始帖子尽可能清楚地说明了一个包含十个类和数千行代码的程序的问题。无需您无限滚动,它就可以做到最好。 不,不是。您的TreeVisitor 正在谈论BinarySearchTree,这是一种不知从何而来的类型。 Tree 应该是 BinarySearchTree 吗?几乎你所有的delete_ 函数都与问题无关,你怎么能得到它? TreeNode的成员是否与问题有关?我对此表示怀疑。一个简短的、自包含的编译示例的全部意义在于,您实际上编写了编译并演示问题的代码,并且在仍然演示问题的同时,您可以从中删除所有可以消除的内容。你可以做得更好。 并且说“将构造函数移到这里”是没有帮助的。如果它们不重要,请忽略它们。如果它们很重要,请包括它们,并尽可能多地从它们中删除,直到它们只包含重要的部分。然后重复。 @Yakk 将 BinarySearchTree 更正为 Tree 并发布了 SSCCE。我试图提供的不是 SSCCE,而是我正在编写的一个非常大的程序的可理解的细分。很抱歉你的例外,并真诚地向你道歉! 【参考方案1】:

关于对boost::bind() 的调用,您应该在通过引用将对象传递给按值接受相应参数的函数模板时使用boost::ref(),否则将尝试复制(这在这种情况下会导致编译器错误,因为无法访问复制构造函数):

boost::bind(TreeVisitor(), boost::ref(tree), val, keyIndex);
//                         ^^^^^^^^^^^^^^^^

但是,这里有一个更大的问题:boost::variant 只能保存可复制构造的类型。来自Boost.Variant online documentation:

对有界类型的要求如下:

CopyConstructible [20.1.3]。

析构函数支持不抛出异常的安全保证。

在变体模板实例化点完成。 (有关接受不完整类型以启用递归变体类型的类型包装器,请参阅 boost::recursive_wrapper&lt;T&gt;。)

指定为variant 的模板参数的每种类型都必须至少满足上述要求。 [...]

【讨论】:

我使用std::ref包裹tree,编译器发出error C2558: class 'boost::_bi::list3&lt;A1,A2,A3&gt;' : no copy constructor available or copy constructor is declared 'explicit'`with A1=boost::_bi::value<:reference_wrapper> A2=boost:: _bi::value, A3=boost::_bi::value @Mushy:那boost::ref 怎么样?如果这不起作用,我将删除此答案 似乎工作;编译器声明 error C2248: 'Tree&lt;T&gt;::Tree' : cannot access private member declared in class 'Tree&lt;T&gt;' 并且 tree = stringTree 存在问题,我认为这可能需要 std::move @Mushy:是的,确实如此。 tree = stringTree 将尝试复制构造 我认为CopyConstructible 的要求是一个文档错误——来自Boost 1.53.0 change notes:“变体:添加了右值构造函数和右值赋值运算符(仅适用于与 C++11 兼容的编译器)。库现在可以与仅移动类型一起使用。"【参考方案2】:
using Mixed = boost::variant< 
  std::unique_ptr<char>,
  std::unique_ptr<short>,
  std::unique_ptr<int>,
  std::unique_ptr<unsigned long>
>;

int main()     
  auto md = std::unique_ptr<int>(new int(123));
  Mixed mixed = std::move(md);
  std::cout << *boost::get< std::unique_ptr<int> >(mixed) << std::endl;
  return 0;

unique_ptr 是只能移动的,可以在变体中使用。上面的例子可以编译和工作(C++11)。

【讨论】:

以上是关于提升::变体; std::unique_ptr 和复制的主要内容,如果未能解决你的问题,请参考以下文章

如何创建可调整大小和固定大小容器的变体

一些 std::unique_ptr 使用和“陷阱”

`std::optional` 比 `std::shared_ptr` 和 `std::unique_ptr` 有啥优势?

std :: list可以包含不同的std :: unique_ptr ?

std::unique_ptr::get() 的奇怪返回行为

是否保证 std::unique_ptr 删除顺序?