Prev: shared_ptr declaration in if-statement ?
Next: sizeof: Why parentheses needed when used with data types (when not required with variables)
From: Trevor Vaughan on 11 Nov 2009 08:00 Hi, In the book _C++ Templates: The Complete Guide_ (2003), Vandevoorde and Josuttis devote section 16.1 (pages 285-9) to a technique for implementing "Named Template Arguments". The full code is too long to quote, but can be accessed at the book's website: http://www.josuttis.com/tmplbook/examples.html, in the file "inherit/namedtmpl.cpp". I have compiled this using gcc [g++-4 (GCC) 4.3.2 20080827 (beta) 2], and confirmed that the code compiles without errors or warnings and runs correctly. At the heart of the code are the following class definitions: // default policies class DefaultPolicy1 {}; class DefaultPolicy2 {}; class DefaultPolicy3 { public: static void doPrint() { std::cout << "DefaultPolicy3::doPrint()\n"; } }; class DefaultPolicy4 {}; // define default policies as P1, P2, P3, P4 class DefaultPolicies { public: typedef DefaultPolicy1 P1; typedef DefaultPolicy2 P2; typedef DefaultPolicy3 P3; typedef DefaultPolicy4 P4; }; template <typename Policy> class Policy3_is : virtual public DefaultPolicies { public: typedef Policy P3; // overriding typedef }; In an attempt to understand how the code works, I tried removing the 'virtual' specifier from class Policy3_is, and this produced a long compiler error, the gist of which was "error: no type named 'P3'". A search on the Net (including the FAQ) yielded much information on virtual inheritance -- the 'dreaded diamond', special constructor syntax, object layout with pointers to the common base object -- but all of this relates to class data members, and the above classes contain no data, only typedefs. So, can someone please explain how (and why) virtual inheritance changes the semantics of classes when these classes contain no data members (or even methods, for that matter), only typedefs? And why this missing ingredient seems to be left out of all the standard explanations of virtual inheritance? Thanks a lot, Trevor -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Joe Smith on 11 Nov 2009 15:44 "Trevor Vaughan" <clcppm-poster(a)this.is.invalid> wrote in message news:op.u28embegxofjdi(a)trevor-pc... > > Hi, > > In the book _C++ Templates: The Complete Guide_ (2003), Vandevoorde > and Josuttis devote section 16.1 (pages 285-9) to a technique for > implementing "Named Template Arguments". The full code is too long to > quote, but can be accessed at the book's website: > http://www.josuttis.com/tmplbook/examples.html, > in the file "inherit/namedtmpl.cpp". > > [snip] > > In an attempt to understand how the code works, I tried removing the > 'virtual' specifier from class Policy3_is, and this produced a long > compiler error, the gist of which was "error: no type named 'P3'". > The following code (without virtual) compiles without any issue on Comeau C++, with C++0x extentions DISabled. ---BEGIN--- #include <iostream> // default policies class DefaultPolicy1 {}; class DefaultPolicy2 {}; class DefaultPolicy3 { public: static void doPrint() { std::cout << "DefaultPolicy3::doPrint()\n"; } }; class DefaultPolicy4 {}; // define default policies as P1, P2, P3, P4 class DefaultPolicies { public: typedef DefaultPolicy1 P1; typedef DefaultPolicy2 P2; typedef DefaultPolicy3 P3; typedef DefaultPolicy4 P4; }; template <typename Policy> class Policy3_is : public DefaultPolicies { public: typedef Policy P3; // overriding typedef }; int main() { Policy3_is<int> a; } ---END--- The problem is that you oversimplified your test case, and misinerpreted the error message. Here is there error message commeau C++ gives on the full version if I omit the "virtual": "ComeauTest.c", line 88: error: "PolicySelector<Setter1, Setter2, Setter3, Setter4>::P3 [with Setter1=Policy3_is<CustomPolicy>, Setter2=DefaultPolicyArgs, Setter3=DefaultPolicyArgs, Setter4=DefaultPolicyArgs]" is ambiguous Policies::P3::doPrint(); ^ detected during instantiation of "void BreadSlicer<PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4>::print() [with PolicySetter1=Policy3_is<CustomPolicy>, PolicySetter2=DefaultPolicyArgs, PolicySetter3=DefaultPolicyArgs, PolicySetter4=DefaultPolicyArgs]" at line 108 That states the problem pretty clearly. Your problem is that without the Virtual, in BreadSlicer::print, there are two types named P3 in scope. I'm not enough of an expert to say why the virtual makes a difference, so I post below a slight abridgment of the full code that still shows the issue. DISCLAIMER: This is not my code, originating instead with _C++ Templates: The Complete Guide_ (2003). ----BEGIN---- #include <iostream> template <typename Base, int D> class Discriminator : public Base { }; template <typename Setter3, typename Setter4> class PolicySelector : public Discriminator<Setter3,3>, public Discriminator<Setter4,4> { }; class DefaultPolicy3 { public: static void doPrint() { std::cout << "DefaultPolicy3::doPrint()\n"; } }; class DefaultPolicy4 {}; class DefaultPolicies { public: typedef DefaultPolicy3 P3; typedef DefaultPolicy4 P4; }; class DefaultPolicyArgs : virtual public DefaultPolicies { }; template <typename Policy> class Policy3_is : /*virtual*/ public DefaultPolicies { public: typedef Policy P3; // overriding typedef }; template <typename PolicySetter3 = DefaultPolicyArgs, typename PolicySetter4 = DefaultPolicyArgs> class BreadSlicer { typedef PolicySelector<PolicySetter3, PolicySetter4> Policies; public: void print () { Policies::P3::doPrint(); } }; class CustomPolicy { public: static void doPrint() { std::cout << "CustomPolicy::doPrint()\n"; } }; int main() { BreadSlicer<> bc1; bc1.print(); BreadSlicer<Policy3_is<CustomPolicy> > bc2; bc2.print(); } ---END--- -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Trevor Vaughan on 12 Nov 2009 01:35 Joe Smith <unknown_kev_cat(a)hotmail.com> wrote: > The problem is that you oversimplified your test case, and misinerpreted > the error message. > > Here is there error message commeau C++ gives on the full version if I > omit > the "virtual": > > "ComeauTest.c", line 88: error: "PolicySelector<Setter1, Setter2, > Setter3, > Setter4>::P3 [with Setter1=Policy3_is<CustomPolicy>, > Setter2=DefaultPolicyArgs, Setter3=DefaultPolicyArgs, > Setter4=DefaultPolicyArgs]" is ambiguous > Policies::P3::doPrint(); > ^ > detected during instantiation of "void > BreadSlicer<PolicySetter1, > PolicySetter2, PolicySetter3, PolicySetter4>::print() > [with PolicySetter1=Policy3_is<CustomPolicy>, > PolicySetter2=DefaultPolicyArgs, > PolicySetter3=DefaultPolicyArgs, > PolicySetter4=DefaultPolicyArgs]" at line 108 > > That states the problem pretty clearly. Your problem is that without the > Virtual, > in BreadSlicer::print, there are two types named P3 in scope. I'm not > enough > of an expert to say why the virtual makes a difference, so I post below a > slight abridgment of the full code that still shows the issue. Thanks a lot for clarifying the problem. For the record, here is the compiler error I get from g++-4 when compiling your abridged version of the full code: .../source/ComeauTest.cpp: In member function 'void BreadSlicer<PolicySetter3, PolicySetter4>::print() [with PolicySetter3 = Policy3_is<CustomPolicy>, PolicySetter4 = DefaultPolicyArgs]': .../source/ComeauTest.cpp:59: instantiated from here .../source/ComeauTest.cpp:42: error: no type named 'P3' in 'class PolicySelector<Policy3_is<CustomPolicy>, DefaultPolicyArgs>' The diagnostic from the Comeau C++ compiler ("... P3 ... is ambiguous") is certainly much more helpful than the "no type named 'P3'" I get from g++! But I hope someone will explain the role 'virtual' is playing here. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Joe Smith on 12 Nov 2009 09:18 "Trevor Vaughan" <clcppm-poster(a)this.is.invalid> wrote in message news:op.u3ae9s0ixofjdi(a)trevor-pc... > > Joe Smith <unknown_kev_cat(a)hotmail.com> wrote: > >> The problem is that you oversimplified your test case, and misinerpreted >> the error message. >> >> Here is there error message commeau C++ gives on the full version if I >> omit >> the "virtual": >> >> "ComeauTest.c", line 88: error: "PolicySelector<Setter1, Setter2, >> Setter3, >> Setter4>::P3 [with Setter1=Policy3_is<CustomPolicy>, >> Setter2=DefaultPolicyArgs, Setter3=DefaultPolicyArgs, >> Setter4=DefaultPolicyArgs]" is ambiguous >> Policies::P3::doPrint(); >> ^ >> detected during instantiation of "void >> BreadSlicer<PolicySetter1, >> PolicySetter2, PolicySetter3, PolicySetter4>::print() >> [with PolicySetter1=Policy3_is<CustomPolicy>, >> PolicySetter2=DefaultPolicyArgs, >> PolicySetter3=DefaultPolicyArgs, >> PolicySetter4=DefaultPolicyArgs]" at line 108 >> >> That states the problem pretty clearly. Your problem is that without the >> Virtual, >> in BreadSlicer::print, there are two types named P3 in scope. I'm not >> enough >> of an expert to say why the virtual makes a difference, so I post below a >> slight abridgment of the full code that still shows the issue. > > Thanks a lot for clarifying the problem. For the record, here is the > compiler error I get from g++-4 when compiling your abridged version > of the full code: > > ../source/ComeauTest.cpp: In member function > 'void BreadSlicer<PolicySetter3, PolicySetter4>::print() > [with PolicySetter3 = Policy3_is<CustomPolicy>, > PolicySetter4 = DefaultPolicyArgs]': > ../source/ComeauTest.cpp:59: instantiated from here > ../source/ComeauTest.cpp:42: error: no type named 'P3' in > 'class PolicySelector<Policy3_is<CustomPolicy>, DefaultPolicyArgs>' > > The diagnostic from the Comeau C++ compiler ("... P3 ... is ambiguous") > is certainly much more helpful than the "no type named 'P3'" I get > from g++! Yuck. I'm reporting this as a bug in g++, since that error message is so misleading it is not even funny. This is now gcc bug 42021: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42021 If it helps, I shrunk this down to a 32 line test case that does not use templates. This may make the class relations more clear. The issue at stake is why both C1 and C2 must use virtual inheritance to avoid error. ----BEGIN--- struct Policy1 { static void doPrint() {} }; struct Policy2 { static void doPrint() {} }; struct B { typedef Policy1 P3; }; struct C1 : /*virtual*/ B { typedef Policy2 P3; }; struct C2 : /*virtual*/ B { }; struct PolicySelector: C1, C2 { }; struct BreadSlicer { typedef PolicySelector Policies; void print () { Policies::P3::doPrint(); } }; int main() { BreadSlicer bc2; bc2.print(); } ---END--- Here are the errors each compile gives under this. Visual C++ v8: >sourceFile.cpp(27) : error C2385: ambiguous access of 'P3' > could be the 'P3' in base 'C1' > or could be the 'P3' in base 'B' (A suprisingly clear and helpful error message.) Comeau/EDG: >"sourceFile.cpp", line 27: error: "PolicySelector::P3" is ambiguous > Policies::P3::doPrint(); > ^ G++: >sourceFile.cpp: In member function `void BreadSlicer::print()': >sourceFile.cpp:27: no type named `P3' in `struct PolicySelector' -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: David Vandevoorde on 13 Nov 2009 03:42
On Nov 12, 1:35 pm, "Trevor Vaughan" <clcppm-pos...(a)this.is.invalid> wrote: > Joe Smith <unknown_kev_...(a)hotmail.com> wrote: > > The problem is that you oversimplified your test case, and misinerpreted > > the error message. > > > Here is there error message commeau C++ gives on the full version if I > > omit > > the "virtual": > > > "ComeauTest.c", line 88: error: "PolicySelector<Setter1, Setter2, > > Setter3, > > Setter4>::P3 [with Setter1=Policy3_is<CustomPolicy>, > > Setter2=DefaultPolicyArgs, Setter3=DefaultPolicyArgs, > > Setter4=DefaultPolicyArgs]" is ambiguous > > Policies::P3::doPrint(); > > ^ > > detected during instantiation of "void > > BreadSlicer<PolicySetter1, > > PolicySetter2, PolicySetter3, PolicySetter4>::print() > > [with PolicySetter1=Policy3_is<CustomPolicy>, > > PolicySetter2=DefaultPolicyArgs, > > PolicySetter3=DefaultPolicyArgs, > > PolicySetter4=DefaultPolicyArgs]" at line 108 > > > That states the problem pretty clearly. Your problem is that without the > > Virtual, > > in BreadSlicer::print, there are two types named P3 in scope. I'm not > > enough > > of an expert to say why the virtual makes a difference, so I post below a > > slight abridgment of the full code that still shows the issue. [...] > The diagnostic from the Comeau C++ compiler ("... P3 ... is ambiguous") > is certainly much more helpful than the "no type named 'P3'" I get > from g++! > > But I hope someone will explain the role 'virtual' is playing here. Virtual inheritance ensures that there is only one base class DefaultPolicies (in the original example; see also Figure 16.1 in the book). When you write Policies::P3 in the BreadSlicer template, a lookup is performed and finds two types P3: The one in Policy3_is (found once) and the one in DefaultPolicies (found three times, via Discriminator<..., 2>, Discriminator<..., 3>, and Discriminator<..., 4>). However, DefaultPolicies is always a base class of Policy3_is, so the "domination rule" applies and the type from the more derived class is selected (i.e., Policy3_is). Now, if ordinary inheritance is used instead, we find the same types again, but this time the DefaultPolicies in which we find P3 (three times) are base classes of Policy3_is. So the dominance rule does not apply and the compiler cannot choose between the two P3 types. I hope that helps. Daveed -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |