std::ostream : класс недоступен [C++]

Я получаю эту ошибку в своей реализации:

структура bookdatabase::Bookdatabase::Book

класс "bookdatabase::BookDatabase::Book" недоступен

Ни одно из следующих решений не решило мою проблему:

Вот изображение того, с чем у Visual Studio возникли проблемы в файле .cpp.

Вот изображение объявления в заголовочном файле.


База данных.h

#include <string>
#include <vector>


#ifndef BOOKDATABASE_H
#define BOOKDATABASE_H

namespace bookdatabase {
    class BookDatabase {
    private:
        struct Book {
        private:
            std::string authorFirstName, authorLastName, authorFullName, bookTitle, pubDate;
        public:
            Book(const std::string &authFirst, const std::string &authLast, const std::string &title, const std::string &date);
            std::string getAuthor() const;
            std::string getBookTitle() const;
            std::string getPubDate() const;
            bool operator < (const Book &rhs) const;
            friend std::ostream& operator << (std::ostream& out, const bookdatabase::BookDatabase::Book& book);
        };
        void sortBooks();
        std::vector<Book>::iterator search(const std::string &title);
    public:
        BookDatabase();
        void printBookList();
        std::vector<Book> getDatabase() const;
        void removeBook(const std::string &title);

        void addBook(const std::string &authFirst, const std::string &authLast, const std::string &title, const std::string &date);
    private:
        std::vector<Book> database;
    };
}

#endif // BOOKDATABASE_H


База данных.cpp

std::ostream & bookdatabase::operator<<(std::ostream & out, const bookdatabase::BookDatabase::Book & book) {
    out << authorFullName << ". " << bookTitle << ". " << pubDate;
    return out;
}


У меня возникла эта проблема, потому что класс Book является вложенным классом?


person James Patmon    schedule 03.12.2017    source источник
comment
У меня возникла эта проблема, потому что класс Book является вложенным классом? Нет, у вас возникла эта проблема, потому что это вложенный класс private.   -  person user0042    schedule 03.12.2017
comment
@ user0042 Спасибо. Насколько я понимаю, если оператор перегрузки является другом класса, он будет иметь доступ к его закрытым членам. Разве это не применимо, если класс частный? Имеет ли смысл сделать оператор другом класса Database вместо доступа к классу Book?   -  person James Patmon    schedule 03.12.2017


Ответы (2)


Хорошо, кажется, я обнаружил здесь две вещи, которые могут быть проблемой, с которой вы столкнулись, одна из которых может быть ошибкой в ​​Visual Studio.

1. В первую очередь то, что не является багом

user0042, в своем ответе на ваш пост прав, operator<<, объявленный как friend в struct Book, приводит к тому, что operator<< может получить доступ только к закрытым членам Book. Но он не может получить доступ к самому Book, потому что Book является закрытым членом окружающего класса BookDatabase. Поэтому вам нужно переместить объявление друга за пределы Book и в BookDatabase.

Как только вы это сделаете, operator<< сможет получить доступ к члену личных данных Book из BookDatabase. Однако обратите внимание, что это не дает operator<< разрешения на доступ к закрытым элементам данных самого Book, а именно authorFirstName, authorLastName, authorFullName, bookTitle, pubDate.

2А. Теперь о (чем я полагаю) ошибке оператора прицела VS2017.

Допустим, вы переместили объявление друга в BookDatabase, вам все еще нужно определить реализацию определенным образом. Следующие два фрагмента кода должны быть эквивалентными способами определения функции в файле Database.cpp, но один из них не работает в VS2017 15.4.5.

Это нормально

namespace bookdatabase {
    ostream& operator<<(ostream& out, const bookdatabase::BookDatabase::Book& book) {
    }
}  // OK

Но это недопустимо

ostream& bookdatabase::operator<<(ostream& out, const bookdatabase::BookDatabase::Book& book) {
}  // Visual Studio cannot access book

Кажется, существует проблема с тем, что оператор :: Visual Studio может получить свойство friend функции.

Итак, чтобы ответить на ваш вопрос: если вы хотите, чтобы перегруженный оператор ‹‹ работал, вам нужно использовать первый метод, чтобы определить его в файле реализации .cpp.

2Б. На самом деле я написал простую тестовую программу, чтобы показать ошибку friend и :: VS2017.

namespace my_namespace {
    class Test {
    private:
        struct Nested {};
    public:
        friend void func(Test::Nested&);
    };
    void func(Test::Nested&);
}

// DOES NOT COMPILE in VS2017 15.4.5, OK in GCC 6.3 and Clang 3.8.0
// VS2017 says Nested is inaccessible
void my_namespace::func(my_namespace::Test::Nested&) {
} 

Но использование namespace и скобок работает

namespace my_namespace {
    class Test {
    private:
        struct Nested {};
    public:
        friend void func(Test::Nested&);
    };
    void func(Test::Nested&);
}

// changed to namespace + brackets, 
// COMPILES in VS2017 15.4.5, GCC 6.3 and Clang 3.8.0
namespace my_namespace {
    void func(my_namespace::Test::Nested&) {
    } 
}

Может кто-нибудь, пожалуйста, независимо проверить это?

Я также опубликовал отчет об ошибке в сообществе разработчиков Microsoft

person L.Y. Sim    schedule 03.12.2017
comment
Я переместил функцию друга operator<< в класс базы данных и удалил оператор разрешения области пространства имен bookdatabase, решив вместо этого поместить реализацию operator<< в блок пространства имен (как показано в вашем посте). К сожалению, я все еще получаю сообщение об ошибке, что вложенный класс Book недоступен. Я думаю, что единственным решением может быть сделать общедоступным вложенный класс Book. - person James Patmon; 03.12.2017
comment
Странно, у вас нормально компилируется? Мой нормально компилируется. Можете ли вы показать мне, что пошло не так? Если вы пытаетесь получить доступ к членам данных Book из тела функции, вы не можете этого сделать, потому что operator<< не является другом структуры Book. - person L.Y. Sim; 03.12.2017

Мой ответ на большинство проблем с оператором ‹‹ состоит в том, чтобы объявить членский метод print(), который выполняет тяжелую работу, и вызвать его из оператора ‹‹. Обычно я рад, что метод печати является общедоступным и избавляет от всех гадостей друзей.

person Gem Taylor    schedule 04.12.2017