На что ссылается конструктор копирования? - PullRequest
3 голосов
/ 06 августа 2020

В зависимости от того, как я создаю свой объект Log, я получаю сообщение об ошибке компилятора, в котором говорится, что я пытаюсь сослаться на удаленную функцию (конструктор копирования):

C: \ Projects \ Logger \ src \ Logger. cpp (34,1): ошибка C2280: 'Logger :: Log :: Log (const Logger :: Log &)': попытка ссылки на удаленную функцию [C: \ Projects \ Logger \ build \ Logger.vcxproj]

Ожидается, что мой объект журнала неявно удалил конструктор копирования, потому что:

T имеет non-stati c элементы данных, которые не могут быть скопированы (имеют удаленные, недоступные или неоднозначные конструкторы копирования);

Это имеет смысл из-за std::ofstream, у которого удален конструктор копирования .

Я не могу понять, почему вообще вызывается конструктор копирования. Как насчет построения с оператором присваивания, который вызывает конструктор копирования? Я компилирую с помощью MSV C, есть ли какой-нибудь флаг компилятора, который я не использую, который не оптимизирует определенное поведение? Например, создать временный объект, затем скопировать конструкцию в именованный объект 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;
}

Интерфейс класса 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;
        }
    };
}

Ответы [ 3 ]

2 голосов
/ 06 августа 2020

Дело не только в ненужной копии и C ++ 17, мы знаем, что

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

отлично работает, но почему это помечает ошибку C2280

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

проблема в офстриме это в классе, я получил ваш код и скомпилировал его, у меня были сомнения по поводу объекта ofstream, я удалил его, и он отлично работает, также я сделал его, указатель также работает нормально, затем я попробовал этот код:

 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> &) [с _Elem = char, _Traits = std :: char_traits] "(объявленный в строке 1080" C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Enterprise \ VC \ Tools \ MSVC \ 14.25.28610 \ include \ fstream ") не может быть указан - это удаленная функция

, что означает, что из потока предотвращает конструктор копирования и оператор =, чтобы избежать доступа к одним и тем же данным (файлу), и это имеет место в вашем коде, когда вы вызываете копию конструктор этой строкой

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

, неявно используя конструктор копирования по умолчанию, он копирует поле за полем с помощью оператора = для каждого объекта, который у вас есть, или примитивного типа, и это случай с ofstream, который предотвращает оператор =, а затем компилятор отмечает ошибку вызова удаленной функции -

для решения этой проблемы вы можете объявить указатель потока, используя необработанные или умные указатели и управлять памятью, или вы также можете создать свою версию конструктора перемещения с вашей настройкой - что также удалит конструктор копирования по умолчанию в вашем классе - и постарайтесь избежать доступа к одним и тем же данным по разным потокам.

1 голос
/ 06 августа 2020

Вы должны вызвать конструктор следующим образом Logger::Log foo("sample.log"), потому что то, что вы делаете, создаст ненужную копию, что не нравится компиляторам до C ++ 17.

1 голос
/ 06 августа 2020
Logger::Log foo = Logger::Log::Log("sample.log");

В этой строке сначала создается Log и копируется в foo.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...