从 unique_ptr 返回引用有时会创建其对象的副本吗?

Does returning a reference from a unique_ptr sometimes create a copy of its object?

下面的代码使用地图中 unique_ptr 的引用来填充对象。 unique_ptr 的后续参考显示没有更新的痕迹。

XSSFWorkbook::CreateCellStyleXSSFCellStyle::CreateCellStyle 工厂函数的客户端。它将 XSSFCellStyle 对象存储在 UPTR__CellStylesMap 中,return 是对它的引用。

XSSFWorkbook::GetStylesheetXml() 稍后获得对每个 XSSFCellStyle 的引用并使用它来输出 XML。

XSSFWorkbook.cpp

//  ****************************************************************************
//  CreateCellStyle
//  ****************************************************************************
    XSSFCellStyle&
    XSSFWorkbook::CreateCellStyle()
    {   map<int, unique_ptr<XSSFCellStyle>>::iterator   iter__CellStylesMap;
        unique_ptr<XSSFCellStyle>                       uptr__XSSFCellStyle     = nullptr;

        uptr__XSSFCellStyle = XSSFCellStyle::CreateCellStyle();
        UPTR__CellStylesMap->insert( make_pair( UPTR__CellStylesMap->size(), move( uptr__XSSFCellStyle ) ) );
        iter__CellStylesMap = UPTR__CellStylesMap->find( UPTR__CellStylesMap->size() - 1 );
        return *iter__CellStylesMap->second
    }

//  ****************************************************************************
//  GetStylesheetXml
//  ****************************************************************************
    string
    XSSFWorkbook::GetStylesheetXml()
    {   map<int, unique_ptr<XSSFCellStyle>>::iterator   iter__CellStylesMap;
        string                                          str___CellStyleXfsXml   = "";

//      ========================================================================
//      Build the XML for the Cell Styles.
//      ========================================================================
        if( UPTR__CellStylesMap->size() > 0)
        {   str___CellStyleXfsXml   += "<cellStyleXfs count=\"" + ConvertToString( UPTR__CellStylesMap->size() ) + "\">";
            iter__CellStylesMap      = UPTR__CellStylesMap->begin();
            while( iter__CellStylesMap != UPTR__CellStylesMap->end() )
//          {   str___CellStyleXfsXml           +=  iter__CellStylesMap->second->GetXml();
            {   XSSFCellStyle& xssf__CellStyle   = *iter__CellStylesMap->second;
                str___CellStyleXfsXml           +=  xssf__CellStyle.GetXml();
                iter__CellStylesMap++;
            }
            str___CellStyleXfsXml   += "</cellStyleXfs>";
...
    }

/   ****************************************************************************
//  GetXml
//  ****************************************************************************
    string
    XSSFWorkbook::GetXml()
    {   string  str___WorkbookXml   = "";

        str___WorkbookXml   += "<workbook><sheets><sheet name=\"1\" sheetId=\"1\" r:id=\"rId1\" /></sheets></workbook>";
        str___WorkbookXml   += GetStylesheetXml();
...
        return str___WorkbookXml;
    }

WinMain 中的这段代码调用 XSSFWorkbook::GetXml(),然后调用 XSSFWorkbook::GetStylesheetXml() 获取对 XSSFCellStyle 对象的引用并打印其 XML.

XSSFCellStyle xssf__CellStyle = xssf__Workbook.CreateCellStyle();
xssf__CellStyle.SetApplyAlignment( true );
uptr__OoxmlTester->SetOoxml( xssf__Workbook.GetXml() );

它return编辑了 XSSFCellStyle::XSSFCellStyle() 初始化列表中设置的默认值,即使 cout 语句显示 XXSFCellStyle: :SetApplyAlignment() 正确设置 ApplyAlignment 属性。

但改用 XSSFCellStyle 参考

uptr__OoxmlTester->SetOoxml( xssf__CellStyle.GetXml() );

做了 return更新值(当然)。

更改 XSSFWorkbook::CreateCellStyle() 使其return成为原始指针

XSSFCellStyle*
XSSFWorkbook::CreateCellStyle()
...
return iter__CellStylesMap->second.get();

WinMain同样

XSSFCellStyle* ptr___CellStyle = xssf__Workbook.CreateCellStyle();
ptr___CellStyle->SetApplyAlignment( true );
uptr__OoxmlTester->SetOoxml( xssf__Workbook.GetXml() );

返回了更新后的值,表明指针版本确实更新了 UPTR__CellStylesMap 中的 XSSFCellStyle 实例。

我不明白 XSSFWorkbook::CreateCellStyle() 的参考版本如何给我一个 XSSFCellStyle 对象的引用在 UPTR__CellStylesMap(副本?)中,而 XSSFWorkbook::GetCellStyleXml() 中的相同代码UPTR__CellStylesMap.

中获取对 XSSFCellStyle 对象的引用

因为没有 NULL XSSFCellStyle 这样的东西,所以我放弃了参考版本,转而使用 unique_ptr 版本,但我仍然喜欢知道如何永远不要重新创造这个谜团。

P.S。我的编码风格打破常规,让一些人感到不安。请不要让它打扰你。

是的,您正在这一行中创建副本:

XSSFCellStyle xssf__CellStyle = xssf__Workbook.CreateCellStyle();

应该注意的是,正如您暗示的那样,副本不是通过 returning 引用创建的,而是通过使用 returned 引用初始化一个新值来创建的。
如果您想避免复制,您应该将结果存储在引用或指针中。

XSSFCellStyle& xssf__CellStyle = xssf__Workbook.CreateCellStyle();
//or this
XSSFCellStyle* ptr__CellStyle = &xssf__Workbook.CreateCellStyle();

如果你想避免意外复制,那么你应该考虑像这样删除 XSSFCellStyle 的复制构造函数:

XSSFCellStyle( const XSSFCellStyle&) = delete;

我知道这是一种风格决定,但我认为以 Create 开头的函数不应该 return 作为参考。