Как реализовать следующее без проблем, связанных с «недопустимым использованием неполного типа»?
class A { // line#10
/*(...) some fields and methods here. */
// more fields of the following functionality exist, storing data about object A's state.
int SomeField;
class Exception {
/*(...) some fields and methods here. */
public: enum ProblemCase { /*(...) */ };
protected: Exception( ProblemCase issue, int additionalData ) { /*(...)*/ } // line#29
friend Exception A::BuildException( ProblemCase ); // line#34
};
inline Exception BuildException( Exception::ProblemCase issue ) {
return Exception( issue, SomeField ); // line#99
}
};
A.hpp:34:72: error: invalid use of incomplete type ‘class A’
A.hpp:10:7: error: forward declaration of ‘class A’
A.hpp: In member function ‘A::Exception A::BuildException(A::Exception::ProblemCase)’:
A.hpp:29:20: error: ‘A::Exception::Exception(A::Exception::ProblemCase, int)’ is protected
A.hpp:99:46: error: within this context
обратите внимание, что имена изменены, поэтому количество символов в журнале ошибок неверно.
Это невозможно с простой перестановкой деклараций. Немного основной требуется реструктуризация.
class A {
public:
class Exception;
private:
class AHelper
{
public:
friend class A;
class ExceptionBase
{
public:
typedef enum {Something} ProblemCase;
};
private:
inline static Exception BuildException(ExceptionBase::ProblemCase issue, int something);
};
int SomeField;
public:
class Exception : public AHelper::ExceptionBase
{
public:
using AHelper::ExceptionBase::ProblemCase;
protected:
Exception(ProblemCase issue, int additionalData) {}
friend Exception AHelper::BuildException(ProblemCase, int);
};
inline Exception BuildException(Exception::ProblemCase issue);
};
inline A::Exception A::AHelper::BuildException(A::Exception::ProblemCase issue, int something)
{
return A::Exception(issue, something);
}
inline A::Exception A::BuildException(A::Exception::ProblemCase issue)
{
return AHelper::BuildException(issue, SomeField);
}
Конструктор для Exception
защищен, что означает, что только классы, полученные из Exception
разрешено использовать это. Класс A
не является производным от Exception
Вот почему вы получаете сообщение об ошибке в строке 99.
Изменить: Хорошо, поэтому я взял приведенный выше код, очистил его, чтобы он был скомпилирован, и я получаю точно такие же сообщения об ошибках — хорошо для воспроизведения!
Решение, которое я мог бы найти, состоит в том, чтобы удалить «защищенный» из конструктора и сделать общедоступным. Я не могу найти другое решение, которое действительно работает, и я не уверен, ПОЧЕМУ оно не работает так, как написано.
class A {
int SomeField;
class Exception
{
public:
enum ProblemCase { PB1, PB2 };
public:
Exception( ProblemCase issue, int additionalData ) { }
};
inline Exception BuildException( Exception::ProblemCase issue ) {
return Exception( issue, SomeField ); // line#99
}
};
Просто как «академический» пример, самый близкий, который я мог получить. Примечание: НЕ рекомендуется.
#include <type_traits>
struct A { // line#10
/*(...) some fields and methods here. */
// more fields of the following functionality exist, storing data about object A's state.
int SomeField;
class Exception;
template < typename T >
inline Exception BuildException( T issue );
class Exception {
/*(...) some fields and methods here. */
public: enum ProblemCase { FIRST /*(...) */ };
protected: Exception( ProblemCase issue, int additionalData ) { /*(...)*/ } // line#29
// accepted by clang, not by g++??
// friend Exception A::BuildException<ProblemCase>( ProblemCase ); // line#34
// accepted by both
template < typename T >
friend Exception A::BuildException( T ); // line#34
};
};
// there's no need to define it outside the class
// (I originally used an explicit specialization)
template < typename T >
A::Exception A::BuildException( T issue ) {
// optional
static_assert(std::is_same<typename std::remove_const<T>::type, Exception::ProblemCase>::value, "Wrong type!");
return Exception( issue, SomeField ); // line#99
}
int main()
{
A a;
a.BuildException(A::Exception::FIRST);
}