Click here to Skip to main content
15,888,160 members
Please Sign up or sign in to vote.
2.00/5 (1 vote)
I had gotten an interesting reference (From: How can I access public nested class methods from base class variables (C++, Visual Studio 2013)?[1]) about the use of the keyword virtual; so I started looking into it a bit, and I found:

Virtual Inner Classes? - C++ Forum[2]

I tried copying that into visual studio and compiling it, and it worked fine; however, I use classes in my code versus struct in the example. Now, I figured that the use of struct and class would be very nearly the same, as long as I define the scope. Being lazy, I set the scope for each class to be defined as public to not incur any penalties. However, the code with the class in it does not compile and induces a lot of errors, versus the code with struct compiles fine.

For the purposes of the test, everything can be considered in one file.

The code that works is:

C#
//Example taken from : http ://www.cplusplus.com/forum/lounge/61329/
//and duplicated here only to test if structures are equivalent to classes with Visual Studio 2013 as well as what to do with noexcept

#include <iostream>
#include <memory>

namespace one
{
	struct A
	{
		struct B
		{
			virtual ~B() {}  //was virtual ~B() noexcept{}, but removed the noexcept keyword to get it to compile
			virtual void foo() = 0;
		};

		std::shared_ptr<B> bar(bool);
	};

	std::shared_ptr<A::B> A::bar(bool f)
	{
		struct C : A::B
		{
			C(int, int) {}
			virtual void foo() override { std::cout << "one::A::bar::C::foo\n"; }
		};

		if (f) return std::make_shared<C>(3, 4);

		else
		{
			struct D : C
			{
				D(int a, int b, int c) : C(a, b) {}
				virtual void foo() override { std::cout << "one::A::bar::D::foo\n"; }
			};
			return std::make_shared<D>(10, 20, 30);
		}
	}
}

int main()
{
	one::A a;
	auto p = a.bar(false);
	p->foo(); // prints one::A::bar::D::foo
}

#endif



The code that does not compile is:

C#
//Example taken from : http ://www.cplusplus.com/forum/lounge/61329/
//and duplicated here only to test if structures are equivalent to classes with Visual Studio 2013 as well as what to do with noexcept

#include <iostream>
#include <memory>

namespace one
{
	struct A
	{
		struct B
		{
			virtual ~B() {}  //was virtual ~B() noexcept{}, but removed the noexcept keyword to get it to compile
			virtual void foo() = 0;
		};

		std::shared_ptr<B> bar(bool);
	};

	std::shared_ptr<A::B> A::bar(bool f)
	{
		struct C : A::B
		{
			C(int, int) {}
			virtual void foo() override { std::cout << "one::A::bar::C::foo\n"; }
		};

		if (f) return std::make_shared<C>(3, 4);

		else
		{
			struct D : C
			{
				D(int a, int b, int c) : C(a, b) {}
				virtual void foo() override { std::cout << "one::A::bar::D::foo\n"; }
			};
			return std::make_shared<D>(10, 20, 30);
		}
	}
}

/*Variation on namespace a from the example, using public classes in stead of struct*/
namespace two
{
	class A
	{
	public:
		class B
		{
		public:
			virtual ~B() {}  //was virtual ~B() noexcept{}, but removed the noexcept keyword to get it to compile
			virtual void foo() = 0;
		};

		std::shared_ptr<B> bar(bool);
	};

	std::shared_ptr<A::B> A::bar(bool f)
	{
		class C : A::B
		{
		public:
			C(int, int) {}
			virtual void foo() override { std::cout << "one::A::bar::C::foo\n"; }
		};

		if (f) return std::make_shared<C>(3, 4);

		else
		{
			class D : C
			{
			public:
				D(int a, int b, int c) : C(a, b) {}
				virtual void foo() override { std::cout << "one::A::bar::D::foo\n"; }
			};
			return std::make_shared<D>(10, 20, 30);
		}
	}
}


int main()
{
	one::A a;
	auto p = a.bar(false);
	p->foo(); // prints one::A::bar::D::foo
}

#endif


The errors are: c2243 (see for detail: Compiler Error C2243[3])

C#
1>------ Build started: Project: bmpLoad, Configuration: Debug Win32 ------
1>  CDCDirectControls.cpp
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(291): error C2243: 'type cast' : conversion from 'two::A::bar::C *' to 'two::A::B *' exists, but is inaccessible
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(577) : see reference to function template instantiation 'std::_Ptr_base<_Ty>::_Ptr_base<two::A::bar::C>(std::_Ptr_base<two::A::bar::C> &&)' being compiled
1>          with
1>          [
1>              _Ty=two::A::B
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(577) : see reference to function template instantiation 'std::_Ptr_base<_Ty>::_Ptr_base<two::A::bar::C>(std::_Ptr_base<two::A::bar::C> &&)' being compiled
1>          with
1>          [
1>              _Ty=two::A::B
1>          ]
1>          c:\users\stephen\desktop\stephen\cppprojects\samplemfc\bmpload\bmpload\cdcdirectcontrols.h(186) : see reference to function template instantiation 'std::shared_ptr<two::A::B>::shared_ptr<two::A::bar::C,void>(std::shared_ptr<two::A::bar::C> &&) throw()' being compiled
1>          c:\users\stephen\desktop\stephen\cppprojects\samplemfc\bmpload\bmpload\cdcdirectcontrols.h(186) : see reference to function template instantiation 'std::shared_ptr<two::A::B>::shared_ptr<two::A::bar::C,void>(std::shared_ptr<two::A::bar::C> &&) throw()' being compiled
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(291): error C2243: 'type cast' : conversion from 'two::A::bar::D *' to 'two::A::B *' exists, but is inaccessible
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(577) : see reference to function template instantiation 'std::_Ptr_base<_Ty>::_Ptr_base<two::A::bar::D>(std::_Ptr_base<two::A::bar::D> &&)' being compiled
1>          with
1>          [
1>              _Ty=two::A::B
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(577) : see reference to function template instantiation 'std::_Ptr_base<_Ty>::_Ptr_base<two::A::bar::D>(std::_Ptr_base<two::A::bar::D> &&)' being compiled
1>          with
1>          [
1>              _Ty=two::A::B
1>          ]
1>          c:\users\stephen\desktop\stephen\cppprojects\samplemfc\bmpload\bmpload\cdcdirectcontrols.h(196) : see reference to function template instantiation 'std::shared_ptr<two::A::B>::shared_ptr<two::A::bar::D,void>(std::shared_ptr<two::A::bar::D> &&) throw()' being compiled
1>          c:\users\stephen\desktop\stephen\cppprojects\samplemfc\bmpload\bmpload\cdcdirectcontrols.h(196) : see reference to function template instantiation 'std::shared_ptr<two::A::B>::shared_ptr<two::A::bar::D,void>(std::shared_ptr<two::A::bar::D> &&) throw()' being compiled
1>  Generating Code...
1>  Skipping... (no relevant changes detected)
1>  bmpLoadView.cpp
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


All the code is self-contained in one file.

1) How the code need to get rewritten using classes?
2) Why does it work with struct but not with class? (I thought the two were identical except for scope, but with public: specifiers, they should act the same.)

Additional references are:

Virtual Inner Classes? - C++ Forum[3]

virtual function specifier - cppreference.com[4]
Posted
Updated 26-Jan-16 1:12am
v4
Comments
Richard MacCutchan 26-Jan-16 7:38am    
Why would anyone write such obfuscated code in the first place?
Aescleal 26-Jan-16 9:29am    
C++ is not JavaScript... This sort of code is the sort of thing an experienced JS programmer would turn out but in C++ the class nesting is essentially overkill and doesn't do anything that a pair of implementation classes in an anonymous namespace wouldn't.

I wouldn't start trying to use code like this until you know why you want to do and that there's no cleaner solution. Have you ever considered using a far simpler design where you don't actually use nested classes? One major design flaw with this sort of "virtual constructor" is that you've prevented yourself creating an instance of your implementation class without using the factory which makes testing far more tedious than it needs to be.

1 solution

While you have made all the members of your classes public you forgot the access specifier for the base class which is private by default. So make that public too:
class C : public A::B
{
// ...
};

class D : public C
{
// ...
};
 
Share this answer
 
Comments
Philippe Mori 26-Jan-16 18:08pm    
And by the way, this is why it works with struct since derivation is also public.
Aescleal 27-Jan-16 4:24am    
Wish I could give you some points for this - this is an aspect of C++ I've never considered, thanks for expanding my mind today!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900