2015-12-16 9 views
4

Я пытаюсь написать неинтрузивные подпрограммы boost :: serialization для вложенного класса с частным членом. К сожалению, я не могу убедить g ++ в том, что процедура сериализации является другом внутреннего класса. Кажется, g ++ требует прямого объявления процедуры сериализации, которая, в свою очередь, потребует прямого объявления вложенного класса, который, в свою очередь, не может быть выполнен на C++. Я что-то упустил или это просто невозможно? clang ++ в отличие от этого не требует прямого объявления и не имеет проблем с приведенным ниже кодом. Следующий код иллюстрирует проблему:Объявление друга в вложенных классах, требующих форвардного объявления

#include <boost/archive/text_oarchive.hpp> 

class Outer; 
//class Outer::Inner; // Not valid C++ 

namespace boost 
{ 
    namespace serialization 
    { 
     template <class Archive> 
     void serialize(Archive &ar, Outer& outer, const unsigned int version); 
     //template <class Archive> 
     //void serialize(Archive &ar, Outer::Inner& inner, const unsigned int version); // Cannot be done since forward declaration of nested class not possible. 
    } 
} 

class Outer 
{ 
    class Inner 
    { 
     int member_{42}; 

     template <class Archive> 
     friend void boost::serialization::serialize(Archive &ar, Outer::Inner &inner, const unsigned int version); // This does not work with gcc since the compiler seems to expect a forward declaration, which cannot be done (see above). 
    }; 

    Inner inner_; 

    template <class Archive> 
    friend void boost::serialization::serialize(Archive &ar, Outer &outer, const unsigned int version); 
    template <class Archive> 
    friend void boost::serialization::serialize(Archive &ar, Inner &inner, const unsigned int version); 
}; 

namespace boost 
{ 
    namespace serialization 
    { 
     template <class Archive> 
     void serialize(Archive &ar, Outer& outer, const unsigned int version) 
     { 
      ar & outer.inner_; 
     } 

     template <class Archive> 
     void serialize(Archive &ar, Outer::Inner& inner, const unsigned int version) 
     { 
      ar & inner.member_; 
     } 
    } 
} 

int main() 
{ 
    Outer outer; 

    boost::archive::text_oarchive(std::cout) << outer; 
} 

Чтобы быть скомпилирован с -std=c++11 и -lboost_serialization. Компиляция с g ++ жалуется, что member_ является закрытым, хотя присутствует декларация друга. Действительно ли g ++ отклоняет объявление друга во внутреннем классе?

ответ

2

[dcl.meaning]/1:

Когда описатель-ID квалифицирован, декларация будет относиться к ранее объявленный член класса или пространства имен, к которому классификатор относится (или, в случай пространства имен, элемента из встроенного пространства имен этого пространства имен ([namespace.def])) или к специализации ; [...].

Другими словами, объявление (включая объявление друга) с квалифицированным именем должно ссылаться на ранее объявленное. Таким образом, GCC является своего рода правильным в отказе от кода, но он должен был отклонить его раньше, и диагностика довольно запутанна. (Обратите внимание, что он будет отклонять его на месте, если предмет, который находится в дружбе, является ранее незанятой простой функцией, а не шаблоном.)

Кроме того, требуя доступа к другу, вид поражает точку неинтрузивной сериализации в первом (что позволит вам сериализовать класс без изменения его определения).

+0

Итак, вы говорите, что да g ++ - это право отказаться от кода, и да, это просто невозможно? – user1225999

+0

"[...] ссылается на [...]" - так что clang ++ тоже не ошибается в принятии кода? Или это означает, что квалифицированное объявление _must_ относится к ранее объявленному члену пространства имен? – user1225999

+0

@ user1225999 «должен» должно быть. Кланг ошибается. –