What does C4250 VC++ warning mean?
The warning is pointing out that if any weak
class operations depend on vbc
virtual operations that are implemented in dominant
, then those operations might change behavior due to the fact that they are bundled in a diamond inheritance hierarchy.
struct base {
virtual int number() { return 0; }
};
struct weak : public virtual base {
void print() { // seems to only depend on base, but depends on dominant
std::cout << number() << std::endl;
}
};
struct dominant : public virtual base {
int number() { return 5; }
};
struct derived : public weak, public dominant {}
int main() {
weak w; w.print(); // 0
derived d; d.print(); // 5
}
That is the behavior that the standard specifies, but it might be surprising for the programmer at times, the weak::print
operation behavior has changed not because of an overridden method above or below in the hierarchy, but by a sibling class in the inheritance hierarchy, when called from derived
. Note that it makes perfect sense from the derived
point of view, it is calling an operation that depends on a virtual method implemented in dominant
.
Visual Studio Compiler warning C4250 ('class1' : inherits 'class2::member' via dominance)
I had the same warning for the following code:
class Interface
{
public:
virtual void A() = 0;
};
class Implementation : public virtual Interface
{
public:
virtual void A() {};
};
class ExtendedInterface : public virtual Interface
{
virtual void B() = 0;
};
class ExtendedImplementation : public ExtendedInterface , public Implementation
{
public:
virtual void B() {};
};
This bug report for Visual C++ 2005 in msdn suggests that this is a known bug that was considered not important enough to fix... They suggest to disable the warning in this case by using a pragma. I think it is safe also in your case, but you should use virtual inheritance as shown in the answer by Gal Goldman.
What causes C4250 (class inherits member via dominance) when using boost serialization with a virtual base class?
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
- macro
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
C++ Inheritance via dominance warning
Everything is absolutely valid. A compiler is allowed to warn about valid code, no problem here. You can try silencing the warning with a using
declaration. If this doesn't work (probably due to an MSVC bug), silence it with a pragma
.
Avoiding an inheritance by dominance warning for a mocked std::fstream class
It turns out that these warnings are a side-effect from some quirks in the Microsoft C++ STL. I've quoted an explanation from the relevant Microsoft Connect issue below.
My solution was simply to implement empty versions of the inherited functions in my mock:
TEST_F(SomeTest, SomethingIsDoneCorrectly)
{
class MockFstream : public std::fstream
{
void _Add_vtordisp1() { } // Required to avoid VC++ warning C4250
void _Add_vtordisp2() { } // Required to avoid VC++ warning C4250
};
MockFstream lMockFstream;
// Expectations and assertions here
}
An explanation from Microsoft about why the warning occurs:
The story here is somewhat complicated. VC has an obscure compiler option, /vd2 (documented at http://msdn.microsoft.com/en-us/library/7sf3txa8.aspx), that fixes an obscure bug involving virtual base classes. By default, VC does something that's slightly nonconformant to the C++ Standard. /vd2 changes VC's behavior to be conformant, but this inherently affects class layout. (This layout difference is why the default hasn't been changed to be conformant - that would break users attempting to mix code compiled with different major versions of VC. Our C++ Standard Library implementation forbids such mixing, but the compiler itself is somewhat more permissive.) So if users want /vd2, they have to compile everything that way.
The twist is that the layout bug (which /vd2 fixes) affects iostreams, which uses virtual base classes, and our iostreams implementation has a separately compiled component (in msvcp100.dll/libcpmt.lib/etc.). When MS builds the STL's DLL/LIB, they're compiled the default way, without /vd2. As a result, people using /vd2 couldn't use iostreams, or they'd get bizarre crashes. Yuck.
So, we added the do-nothing virtual functions _Add_vtordisp1() and _Add_vtordisp2(). Their presence makes VC perform layout completely conformantly, regardless of /vd2 being used or not, and therefore makes iostreams usable both ways.
_Add_vtordisp1() and _Add_vtordisp2() trigger warning C4250, talking about dominance. This warning is actually completely useless - it's saying that the compiler will do exactly what the Standard requires it to do. Therefore, we suppress it in the STL's headers (which must be /W4 /analyze clean). If you're deriving from fstream, you'll need to suppress this warning in your own code.
Inheritance by dominance - is it really bad?
When performing virtual inheritance, it is a bad idea to not explicitly override every member in the most derived class. Else, you are asking for your code to die a horrible death when someone changes one of your base classes that inherits from the virtual base. There's nothing actively wrong with this, your program won't crash or anysuch, but it's a maintainability bad idea. If you want to call the Bar::foo
version, then you should just delegate to it in Quux::foo
.
Diamond-inheritance scenario compiles fine in G++, but produces warnings/errors in VC++/Eclipse
What is the correct way of doing this? Does the code above produce undefined behavior?
The code is perfectly valid. There is no Undefined Behavior here.
An unqualified call of A()
through a DerivedC
class object, will always call DerivedA::A()
, while an unqualified call of B()
through a DerivedC
class object, will always call the DerivedB::B()
instance.
Visual C++ gives you a warning because your code uses a less commonly known feature of virtual Inheritance which may not be obvious to most common users and might surprise them. In this case, the warning should be taken as an Informative Nitpick rather than a warning.
Note that the C++ Standard does not restrict compilers from emitting informative warnings for perfectly valid code.
The documentation for warning C4250 gives an example which tells you why visual C++ chooses to give this warning.
Related Topics
C++ Virtual Function Table Memory Cost
What Does It Mean to Return a Reference
Changing C++ Output Without Changing the Main() Function
Simulating Mouse Clicks on MAC Os X Does Not Work for Some Applications
#Error Please Use the /Md Switch for _Afxdll Builds
How to Determine Sizeof Class with Virtual Functions
How to Store Variant Data in C++
What Is the Array Form of 'Delete'
Why Does Destructor Disable Generation of Implicit Move Methods
Copying Non-Rectangular Roi Opencv
Redirect Both Cout and Stdout to a String in C++ for Unit Testing
What Are Practical Applications of Weak Linking
How to Determine If Returned Pointer Is on the Stack or Heap
How Many Decimal Places Does the Primitive Float and Double Support
Why There Are Three Unexpected Worker Threads When a Win32 Console Application Starts Up
How to Assign a Value to the Pointer 'This' in C++
How Can Adding Code to a Loop Make It Faster
Convert Std::Variant to Another Std::Variant with Super-Set of Types