簡體   English   中英

什么是引用復制構造函數?

[英]What is referencing the copy constructor?

根據我構建Log object 的方式,我收到一個編譯器錯誤,指出我正在嘗試引用已刪除的 function(復制構造函數):

C:\Projects\Logger\src\Logger.cpp(34,1):錯誤 C2280:'Logger::Log::Log(const Logger::Log &)':試圖引用已刪除的 function [C:\Projects \Logger\build\Logger.vcxproj]

預計我的日志 object 已隱式刪除了復制構造函數,因為:

T 具有無法復制的非靜態數據成員(已刪除、不可訪問或不明確的復制構造函數);

這是有道理的,因為std::ofstream復制構造函數已刪除

我無法弄清楚為什么要調用復制構造函數。 使用調用復制構造函數的賦值運算符進行構造是什么? 我正在使用 MSVC 進行編譯,是否有一些我沒有使用的編譯器標志不能優化某些行為? 例如構造臨時 object 然后將構造復制到命名的 object foo

如下代碼塊所示,只有一個構造函數定義。

#include <iostream>
#include <string>
#include <fstream>
#include "Logger.h"
#include "LoggerConfig.h"

Logger::Log::Log(std::string file) : filename{ file }
{
    logFile = std::ofstream(filename, std::ios::out);
    if (logFile.is_open())
    {
        logFile << "This is a log.\n";
    }
    else
    {
        std::cout << "Unable to open filename: " << filename << '\n';
    }

}

Logger::Log::~Log()
{
    // Wait and take write mutex

    // Close file
}

int main()
{
    // report version
    std::cout << " Version " << LOGGER_VERSION_MAJOR << "."
              << LOGGER_VERSION_MINOR << std::endl;

    Logger::Log foo = Logger::Log::Log("sample.log"); // C2280: attemping to reference a deleted function
    //Logger::Log foo("sample.log"); // Works!
    //Logger::Log foo{"sample.log"}; // Works!

    foo.Write(Logger::Log::Level::INFO, "Testing", 123, "hahaha");

    return 0;
}

Class接口logger.h如下:

#pragma once

namespace Logger
{
    class Log
    {
    private:
        std::string filename;
        std::ofstream logFile;

    public:
        enum class Level
        {
            DEBUG,
            INFO,
            WARNING,
            ERROR
        };

        Log(std::string file);
        ~Log();

        template<typename T>
        void Write(Level lvl, T arg)
        {
            logFile << arg;
            return;
        }

        template<typename T, typename... Args>
        void Write(Level lvl, T firstArg, Args... args)
        {
            logFile << firstArg;
            Write(lvl, args...);
            return;
        }
    };
}

這不僅僅是關於不必要的副本和 C++17,我們知道

   Logger::Log foo("sample.log"); 

工作正常,但為什么這會標記 C2280 錯誤

   Logger::Log foo = Logger::Log::Log("sample.log");

問題出在 class 中的 ofstream 中,我得到了你的代碼並編譯了它,我對 ofstream object 有疑問,我刪除了它,它也可以正常工作,我將它設為指針也可以正常工作,然后我嘗試了這個代碼:

 std::ofstream s =  std::ofstream("sample.log", std::ios::out);
 std::ofstream k;
 k = s;

我收到此錯誤 E1776 function “std::basic_ofstream<_Elem, _Traits>::operator=(const std::basic_ofstream<_Elem, _Traits> &) [with _Elem=char, _Traits=std::char_traits]”(聲明於無法引用“C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.25.28610\include\fstream”的第 1080 行 - 它是已刪除的 function

這意味着 ofstream 阻止復制構造函數和 =operator 以避免訪問相同的數據(文件),當您通過此行調用復制構造函數時,您的代碼就是這種情況

 Logger::Log foo = Logger::Log::Log("sample.log");

隱式使用默認復制構造函數,它使用 = 操作符逐個字段復制您擁有的每個 object 或原始類型,ofstream 就是這種情況,它阻止 = 操作符,然后編譯器標記調用已刪除的 function 錯誤 -

要解決這個問題,您可以使用原始指針或智能指針聲明 ofstream 指針並管理 memory 或者您也可以使用您的自定義創建移動構造函數版本——這也將刪除 class 中的默認復制構造函數——並盡量避免使用不同的ofstreams訪問相同的數據。

Logger::Log foo = Logger::Log::Log("sample.log");

此行首先創建一個Log並將其復制到foo

您必須像這樣調用構造函數Logger::Log foo("sample.log")因為您所做的將創建一個不必要的副本,這是 C++17 之前的編譯器不喜歡的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM