compiler error overloading function in CRTP context

  • Follow


I don't understand why the compiler balks at the following:

//---------begin code -----------
template <typename T>
struct Base {
    void foo() const {}
};

struct Derived : public Base<Derived> {
    void foo(const int arg) const {}
};

int main(){
    Derived d;
    d.foo(15);
    d.foo(); //won't compile... says that the "only candidate function
is void Derived::foo(int) "
}
//-----------end code -----------

It seems to me that the Derived::foo(int) should not hide Base::foo(),
but that's what appears to be happening.  I've tried compiling with
IRIX gcc and Linux g++.

Can anyone explain why the compiler won't allow this?

Thanks in advance,
Daniel


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply the.doag (5) 5/14/2007 9:52:49 PM

Daniel wrote:
> I don't understand why the compiler balks at the following:
> 
> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
> };
> 
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
> 
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
> 
> It seems to me that the Derived::foo(int) should not hide Base::foo(),
> but that's what appears to be happening. ...

Derived names always hide base names.  See below.

> Can anyone explain why the compiler won't allow this?

 From 10.2/2:

"... First, every declaration for the name in the class and in each of 
its base class sub-objects is considered.  A member name f in one 
subobject B hides a member name f in a sub-object A if A is a base class 
sub-object of B. Any declarations that are so hidden are eliminated from 
consideration. ..."

If you assign a Base reference to d, then you can call foo() through 
that reference, because the derived class isn't considered then.  You 
could also qualify foo() with Base, so that it calls the right function:

Derived d;
Base & b = d;
b.foo();        // <- okay, Base::foo() is the only name considered
d.foo(15);      // <- okay, Derived::foo(int)
d.foo();        // <- *error*, Derived::foo(int) hides B::foo()
d.Base::foo();  // <- okay, calls Base's foo()

-- 

John Moeller
fishcorn@gmail.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply John 5/15/2007 12:59:18 PM



On Mon, 14 May 2007, Daniel wrote:

> I don't understand why the compiler balks at the following:
>
> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
> };
>
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
>
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
>
> It seems to me that the Derived::foo(int) should not hide Base::foo(),
> but that's what appears to be happening.  I've tried compiling with
> IRIX gcc and Linux g++.

This has nothing to do with CRTP, it's the simple rules of hiding in
normal derivation.

struct Base
{
  void foo() {}
};

struct Derived : Base
{
  void foo(int i) {}
};

int main()
{
  Derived d;
  d.foo(16); // works
  d.foo(); // fails
}


This came up perhaps two weeks ago. When the compiler finds a name it the
derived class, it does overload resolution based on that alone and doesn't
bother to check the base classes, unless the derived class contains a
using declaration for the name.

The reason is to reduce surprises for the programmer introduced by
overloads in deep bases. Consider:

struct Base1
{
  void foo(int i) {}
};

struct Base2 : Base1 { };

// Much code that blurs the relations.

struct Derived : BaseN
{
  void foo(double d) {}
};

int main()
{
  Derived d;
  d.foo(100); // Expects Derived::foo(double) to be called.
}

If the compiler always considered all bases, the programmer would have to
first check that no class in the base graph contains any other function
called foo. That's not a good assumption. Basically, a programmer should
know, only by looking at the most derived class, which function is called
(or at least that he definitely needs to look in the base classes).

The way it is, the behaviour meets the expectation: Derived::foo(double)
is indeed called.

Sebastian Redl

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Sebastian 5/15/2007 1:02:37 PM

Daniel wrote:

>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
> 
> It seems to me that the Derived::foo(int) should not hide Base::foo(),
> but that's what appears to be happening.  I've tried compiling with
> IRIX gcc and Linux g++.
> 
> Can anyone explain why the compiler won't allow this?
> 
Because the only overload for Derived::foo is one that takes an int.
Let me give you a brief lesson on how functions work in C++:

1.  First the name is looked up (Derived::foo)
2.  Then the overloads for this name are checked (only foo(int))
3.  Then the access is checked for that overload
4.  Then if virtual, the final overrider is selected.

Defining a name in the Derived class hides anything else with that
name in base classes.  If you want to bring forward a name from the
base class, you must do so explicitly:

struct Derived : Base {
    using Base::foo;
    void foo(int);
};

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Ron 5/15/2007 1:03:11 PM

On May 15, 6:52 am, Daniel <the.d...@gmail.com> wrote:
> I don't understand why the compiler balks at the following:
>
> template <typename T>
> struct Base {
>     void foo() const {}
> };
>
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
>
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "}
>
> It seems to me that the Derived::foo(int) should not hide Base::foo(),

Oh, but it does. function lookup stops in the first class that has a
member function of the given name, no matter what arguments that
function takes.

To solve your problem you need to add

using Base<Derived>::foo;

within the definition of Derived.

Cheers,
Nicola Musatti


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Nicola 5/15/2007 1:03:21 PM

Daniel wrote:
> I don't understand why the compiler balks at the following:
> 
> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
> };
> 
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
> 
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
> 
> It seems to me that the Derived::foo(int) should not hide Base::foo(),
> but that's what appears to be happening.  I've tried compiling with
> IRIX gcc and Linux g++.
> 
> Can anyone explain why the compiler won't allow this?
> 
> Thanks in advance,
> Daniel
> 
> 
As I answered in the other forum you posted it in, Derived::foo
is indeed supposed to hide Base::foo.   A name declared in a derived
class always hides the same name in the base class.

Adding
	using Base::foo;
to the Derived class will bring it forward.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Ron 5/15/2007 1:03:51 PM

On May 15, 7:52 am, Daniel <the.d...@gmail.com> wrote:
> I don't understand why the compiler balks at the following:
>
> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
>
> };
>
> struct Derived : public Base<Derived> {

Add this line:

    using Base<Derived>::foo;


>     void foo(const int arg) const {}
>
> };
>
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "}
>
> //-----------end code -----------

This problem popped up a few times recently -- try to google this
group for more elaborate answer.

--
Regards,
Alex


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Alex 5/15/2007 1:39:03 PM

Daniel wrote:
> I don't understand why the compiler balks at the following:
> 
> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
> };
> 
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
> 
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
> 
> It seems to me that the Derived::foo(int) should not hide Base::foo(),
> but that's what appears to be happening.  I've tried compiling with
> IRIX gcc and Linux g++.

By overloading (not overriding) functions in a derived class, you hide all
functions of the base class with the same name that you don't override.

Add the following to the Derived class definition to override that
behaviour:
  using Base::foo;

-- 
Thomas
http://www.netmeister.org/news/learn2quote.html

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Thomas 5/15/2007 1:39:15 PM

Daniel <the.doag@gmail.com> writes:

> //---------begin code -----------
> template <typename T>
> struct Base {
>     void foo() const {}
> };
>
> struct Derived : public Base<Derived> {
>     void foo(const int arg) const {}
> };
>
> int main(){
>     Derived d;
>     d.foo(15);
>     d.foo(); //won't compile... says that the "only candidate function
> is void Derived::foo(int) "
> }
> //-----------end code -----------
>
> It seems to me that the Derived::foo(int) should not hide
> Base::foo(), but that's what appears to be happening.

As it should. Overloading occurs within scopes. Once the name foo has
been found in a scope, lesser scopes are not considered.

A using declaration helps:

struct Derived : public Base<Derived> {
    void foo(const int arg) const {}
    using Base<Derived>::foo;
};

Now Base<Derived>::foo() and Derived::foo(int arg) (the const is just
a comment here) are found simultaneously, and overload resolution will
cause Base<Derived>::foo() to be selected if no argument is passed.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Thomas 5/15/2007 1:39:22 PM

Daniel wrote:
> 
> Can anyone explain why the compiler won't allow this?
> 
> Thanks in advance,
> Daniel
> 
> 

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Road 5/15/2007 1:42:29 PM

John Moeller wrote:
> Base & b = d;

Oops, this should be Base<Derived>, not Base.

-- 

John Moeller
fishcorn@gmail.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply John 5/15/2007 9:25:14 PM

10 Replies
93 Views

(page loaded in 0.198 seconds)

Similiar Articles:










7/17/2012 9:35:25 PM


Reply: