当对虚拟基类使用 boost 序列化时,什么原因导致 C4250(类通过支配继承成员)?

发布于 2024-09-30 08:56:50 字数 5835 浏览 8 评论 0原文

VC++编译器警告C4250 'class1':通过统治继承'class2::member' 我很清楚。 (但请参阅此处获取解释。)

我目前遇到的问题是,在序列化类层次结构时收到此警告 有一个带有 boost::serialization 的抽象基类 (1.44.0)。

请注意,我的类不会形成任何可能导致此警告的类钻石继承层次结构,但该警告是由 boost 的实例化引起的: :detail::is_virtual_base_of_impl<...> 序列化派生类的实例时。 (这似乎正在使用 is_virtual_base_of 来自 Boost.TypeTraits。)


下面是在 Visual Studio 2005 上重现该警告的最小代码示例。请注意,代码应按原样放入一个 cpp 文件中,并且应进行编译。

另请注意代码中我用触发警告的注释标记的两点。如果未使用BOOST_CLASS_EXPORT,则不会触发警告,但更有趣的是,如果派生类使用,警告也不会触发虚拟继承! (所以也许我根本不理解 C4250。)

// -- std includes --
#include <iostream>
#include <sstream>
#include <string>

// -- boost serialization --
#define BOOST_SERIALIZATION_DYN_LINK
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>

// Base with serialization support
struct Base
{
  virtual ~Base() {};
  virtual void DoStuff() const {
    std::cout << "Base@[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
  }

  template<class Archive> // serialization support!
  void serialize(Archive & ar, const unsigned int file_version)  { /*empty*/  }
};

// (The only) Specific class with ser. support
struct Concrete2 : virtual/*!C4250!*/ public Base
{
  virtual void DoStuff() const {
    std::cout << "Concrete2@[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
  }

  template<class Archive> // serialization support!
  void serialize(Archive & ar, const unsigned int ver) {
    ar & boost::serialization::base_object<Base>(*this);
    // This is just a test - no members neccessary
    std::cout << "Concrete2::serialize!" << typeid(ar).name() << "\n";
  }
};
// Without guid export -> *no* C4250, even *with* virtual inheritance
// (however, can't be serialized via base class pointer anymore)
BOOST_CLASS_EXPORT(Concrete2); 

BOOST_CLASS_TRACKING(Concrete2, boost::serialization::track_never);

int main() {
  using namespace std;
  Concrete2 obj1;
  obj1.DoStuff();

  // The following test code is not neccessary to generate the warning ...
  // (but is neccessary to show if base-pointer serialization works at runtime)
  Base* ref1 = &obj1;
  ostringstream out_buf;
  boost::archive::text_oarchive out_archive(out_buf);
  out_archive << ref1;
  const string buf = out_buf.str();

  cout << "Serialized obj:\n~~~~\n";
  cout << buf;
  cout << "\n~~~~~\n";

  std::istringstream in_buf(buf);
  boost::archive::text_iarchive in_archive(in_buf);
  // Concrete2 obj2;
  Base* ref2;
  in_archive >> ref2;
  if(ref2)
    ref2->DoStuff();
  delete ref2;
}

这是编译器警告(呃!):

1>...\boost_library-1_44_0\boost\type_traits\is_virtual_base_of.hpp(61) : warning C4250: 'boost::detail::is_virtual_base_of_impl<Base,Derived,tag>::X' : inherits 'Concrete2::Concrete2::DoStuff' via dominance
1>        with
1>        [
1>            Base=type,
1>            Derived=Concrete2,
1>            tag=boost::mpl::bool_<true>
1>        ]
1>        ...\boostserializewarningtest\vbc.cpp(27) : see declaration of 'Concrete2::DoStuff'
...
1>        ...\boost_library-1_44_0\boost\mpl\eval_if.hpp(40) : see reference to class template instantiation 'boost::mpl::if_<T1,T2,T3>' being compiled
1>        with
1>        [
1>            T1=boost::is_virtual_base_of<type,Concrete2>,
1>            T2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1>            T3=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1>        ]
1>        ...\boost_library-1_44_0\boost\serialization\void_cast.hpp(279) : see reference to class template instantiation 'boost::mpl::eval_if<C,F1,F2>' being compiled
1>        with
1>        [
1>            C=boost::is_virtual_base_of<type,Concrete2>,
1>            F1=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1>            F2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1>        ]
1>        ...\boost_library-1_44_0\boost\serialization\base_object.hpp(68) : see reference to function template instantiation 'const boost::serialization::void_cast_detail::void_caster &boost::serialization::void_cast_register<Derived,Base>(const Derived *,const Base *)' being compiled
1>        with
1>        [
1>            Derived=Concrete2,
1>            Base=type
1>        ]
...    
1>        ...\boost_library-1_44_0\boost\serialization\export.hpp(128) : while compiling class template member function 'void boost::archive::detail::`anonymous-namespace'::guid_initializer<T>::export_guid(boost::mpl::false_) const'
1>        with
1>        [
1>            T=Concrete2
1>        ]
1>        ...\boostserializewarningtest\vbc.cpp(40) : see reference to class template instantiation 'boost::archive::detail::`anonymous-namespace'::guid_initializer<T>' being compiled
1>        with
1>        [
1>            T=Concrete2
1>        ]

The meaning of the VC++ compiler warning C4250 'class1' : inherits 'class2::member' via dominance is clear to me. (But see here for an explanation.)

I have currently the problem that I get this warning when serializing a class hierarchy that has an abstract base class with boost::serialization (1.44.0).

Please note that my classes do not form any kind of diamond-like inheritance hierarchy that could cause this warning, but the warning is caused by the instantiation of boost::detail::is_virtual_base_of_impl<...> when serializing instances of derived classes. (Which seems to be using is_virtual_base_of from Boost.TypeTraits.)


Here is a minimal code sample to reproduce the warning on Visual Studio 2005. Note that the code should be dropped as-is into one cpp-file and it should compile.

Note also the two points in the code that I have marked by comments that trigger the warning. If BOOST_CLASS_EXPORTis not used then the warning is not triggerd, but more interestingly the warning is also not triggered, if the derived class does not use virtual inheritance! (So maybe I do not understand C4250 after all.)

// -- std includes --
#include <iostream>
#include <sstream>
#include <string>

// -- boost serialization --
#define BOOST_SERIALIZATION_DYN_LINK
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>

// Base with serialization support
struct Base
{
  virtual ~Base() {};
  virtual void DoStuff() const {
    std::cout << "Base@[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
  }

  template<class Archive> // serialization support!
  void serialize(Archive & ar, const unsigned int file_version)  { /*empty*/  }
};

// (The only) Specific class with ser. support
struct Concrete2 : virtual/*!C4250!*/ public Base
{
  virtual void DoStuff() const {
    std::cout << "Concrete2@[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
  }

  template<class Archive> // serialization support!
  void serialize(Archive & ar, const unsigned int ver) {
    ar & boost::serialization::base_object<Base>(*this);
    // This is just a test - no members neccessary
    std::cout << "Concrete2::serialize!" << typeid(ar).name() << "\n";
  }
};
// Without guid export -> *no* C4250, even *with* virtual inheritance
// (however, can't be serialized via base class pointer anymore)
BOOST_CLASS_EXPORT(Concrete2); 

BOOST_CLASS_TRACKING(Concrete2, boost::serialization::track_never);

int main() {
  using namespace std;
  Concrete2 obj1;
  obj1.DoStuff();

  // The following test code is not neccessary to generate the warning ...
  // (but is neccessary to show if base-pointer serialization works at runtime)
  Base* ref1 = &obj1;
  ostringstream out_buf;
  boost::archive::text_oarchive out_archive(out_buf);
  out_archive << ref1;
  const string buf = out_buf.str();

  cout << "Serialized obj:\n~~~~\n";
  cout << buf;
  cout << "\n~~~~~\n";

  std::istringstream in_buf(buf);
  boost::archive::text_iarchive in_archive(in_buf);
  // Concrete2 obj2;
  Base* ref2;
  in_archive >> ref2;
  if(ref2)
    ref2->DoStuff();
  delete ref2;
}

And here is the compiler warning (ugh!):

1>...\boost_library-1_44_0\boost\type_traits\is_virtual_base_of.hpp(61) : warning C4250: 'boost::detail::is_virtual_base_of_impl<Base,Derived,tag>::X' : inherits 'Concrete2::Concrete2::DoStuff' via dominance
1>        with
1>        [
1>            Base=type,
1>            Derived=Concrete2,
1>            tag=boost::mpl::bool_<true>
1>        ]
1>        ...\boostserializewarningtest\vbc.cpp(27) : see declaration of 'Concrete2::DoStuff'
...
1>        ...\boost_library-1_44_0\boost\mpl\eval_if.hpp(40) : see reference to class template instantiation 'boost::mpl::if_<T1,T2,T3>' being compiled
1>        with
1>        [
1>            T1=boost::is_virtual_base_of<type,Concrete2>,
1>            T2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1>            T3=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1>        ]
1>        ...\boost_library-1_44_0\boost\serialization\void_cast.hpp(279) : see reference to class template instantiation 'boost::mpl::eval_if<C,F1,F2>' being compiled
1>        with
1>        [
1>            C=boost::is_virtual_base_of<type,Concrete2>,
1>            F1=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1>            F2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1>        ]
1>        ...\boost_library-1_44_0\boost\serialization\base_object.hpp(68) : see reference to function template instantiation 'const boost::serialization::void_cast_detail::void_caster &boost::serialization::void_cast_register<Derived,Base>(const Derived *,const Base *)' being compiled
1>        with
1>        [
1>            Derived=Concrete2,
1>            Base=type
1>        ]
...    
1>        ...\boost_library-1_44_0\boost\serialization\export.hpp(128) : while compiling class template member function 'void boost::archive::detail::`anonymous-namespace'::guid_initializer<T>::export_guid(boost::mpl::false_) const'
1>        with
1>        [
1>            T=Concrete2
1>        ]
1>        ...\boostserializewarningtest\vbc.cpp(40) : see reference to class template instantiation 'boost::archive::detail::`anonymous-namespace'::guid_initializer<T>' being compiled
1>        with
1>        [
1>            T=Concrete2
1>        ]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

歌入人心 2024-10-07 08:56:50

原因实际上是来自 boost 类型特征的 is_virtual_base_of 检查。如果检查成功,此检查构造将生成警告 C4250,如以下示例所示:

...
struct base { 
    virtual void mf() { };
};
struct derived_normal : public base { 
    virtual void mf() { };
};
struct derived_virt : virtual public base { 
    virtual void mf() { };
};

int main() {
    using namespace std;

    cout << "boost::is_virtual_base_of<base, derived_normal>::value reports: ";
    // The following line DOES NOT cause C4250
    cout << boost::is_virtual_base_of<base, derived_normal>::value << endl;

    cout << "boost::is_virtual_base_of<base, derived_virt> reports: ";
    // The following line causes C4250:
    cout << boost::is_virtual_base_of<base, derived_virt>::value << endl;
 ...

FWIW,此类型特征工具在 boost 序列化中的用法如下:

  • BOOST_EXPORT_CLASS -> ;
    • BOOST_CLASS_EXPORT_IMPLMENT ->
      • struct guid_initializer(在export.hpp中)->
      • (...) void_cast.hpp / void_cast_register ->这里使用的是is_virtual_base_of

据我所知,在这种情况下警告是完全无害的,可以通过将标头包装在以下内容中来防止:

#pragma warning( push )
#pragma warning( disable : 4250 ) // C4250 - 'class1' : inherits 'class2::member' via dominance
#include ...
#pragma warning( pop ) // C4250

The reason is in fact the is_virtual_base_of check from boost type traits. This check-construct will generate warning C4250 if the check is successful, as can be seen by this example:

...
struct base { 
    virtual void mf() { };
};
struct derived_normal : public base { 
    virtual void mf() { };
};
struct derived_virt : virtual public base { 
    virtual void mf() { };
};

int main() {
    using namespace std;

    cout << "boost::is_virtual_base_of<base, derived_normal>::value reports: ";
    // The following line DOES NOT cause C4250
    cout << boost::is_virtual_base_of<base, derived_normal>::value << endl;

    cout << "boost::is_virtual_base_of<base, derived_virt> reports: ";
    // The following line causes C4250:
    cout << boost::is_virtual_base_of<base, derived_virt>::value << endl;
 ...

FWIW, the usage of this type-traits tool in boost serialization goes like this:

  • macro BOOST_EXPORT_CLASS ->
    • macro BOOST_CLASS_EXPORT_IMPLEMENT ->
      • struct guid_initializer (in export.hpp) ->
      • (...) void_cast.hpp / void_cast_register -> is_virtual_base_of is used here

As far as I can tell the warning is completely harmless in this case and can be prevented by wrapping the header in:

#pragma warning( push )
#pragma warning( disable : 4250 ) // C4250 - 'class1' : inherits 'class2::member' via dominance
#include ...
#pragma warning( pop ) // C4250
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文