Virtual function signature problem

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Harinezumi
    New Member
    • Feb 2008
    • 32

    Virtual function signature problem

    Hi,
    while creating a hierarchy of classes I ran into a strange problem: it seems the compiler doesn't inherit virtual functions with the same name but different signature if only one of them is overridden in a derived class.
    This is the smallest piece of code I could see the problem appear:
    Code:
    class UserType
    {
    public:
        UserType(float x) : x(x) {}
    
        float x;
    };
    
    class Base
    {
    public:
        Base() : data(0) {}
        virtual const UserType& Data() const { return data; }
        virtual void Data(const UserType& value) { data = value; }
    
    private:
        UserType data;
    };
    
    class D1 : public Base
    {
    };
    
    class D2 : public D1
    {
        virtual void Data(const UserType& value) { D1::Data(value); }
    };
    
    int main()
    {
        D2 d;
        d.Data();
        return 0;
    }
    When I try to compile this, I get the following error:

    main.cpp: In function `int main()':
    main.cpp:32: error: no matching function for call to `D2::T()'
    main.cpp:26: error: candidates are: virtual void D2::T(const UserType&)

    Why does this happen? I thought that any public function of a base class can be called in a derived, but in this case something is amiss.

    Thanks in advance,
    Harinezumi
  • Harinezumi
    New Member
    • Feb 2008
    • 32

    #2
    Sorry, I posted the wrong compiler error.
    It actually says this:

    main.cpp: In function `int main()':
    main.cpp:32: error: no matching function for call to `D2::Data()'
    main.cpp:26: error: candidates are: virtual void D2::Data(const UserType&)

    Comment

    • weaknessforcats
      Recognized Expert Expert
      • Mar 2007
      • 9214

      #3
      It says for d2.Data() that there is no Data function that takes 0 arguments.

      Your class is:
      Originally posted by Harinezumi
      class D2 : public D1
      {
      virtual void Data(const UserType& value) { D1::Data(value) ; }
      };
      and you can clearly see that Data requires a UserType& argument.


      AHA! you say but there is an inherited function from Base that takes 0 arguments:
      Originally posted by Harinezumi
      class Base
      {
      public:
      Base() : data(0) {}
      virtual const UserType& Data() const { return data; }
      virtual void Data(const UserType& value) { data = value; }

      private:
      UserType data;
      };
      What you are seeing here is an example of the C++ principle of dominance.

      In a derived class, a member function with the same name as a member function in the base class but with different arguments is said to dominate. Then, when you try to call the base method, the compiler thinks you mean the derived method and your build fails with wrong arguments errors.

      You should avoid this situation.

      In this case, you have to call the bas class method using the scope resolution operator so the compiler knows exactly which Data function you want:
      [code=cpp]
      int main()
      {
      D2 d;
      d.Base::Data(); //OK
      return 0;
      }
      [/code]

      Please note that even if the Base::Data() were private, you would get the same build errors because the access specifiers (public/private/protected) are ingnored when you redefine functions.

      BTW: Public data members as in UserType are a big no-no. I would redesign that class so the data members are private. The reason is that you expose your implementaton. That means thst if you redesign UserType all the code that uses the public data member breaks.

      Comment

      • Harinezumi
        New Member
        • Feb 2008
        • 32

        #4
        Originally posted by weaknessforcats
        What you are seeing here is an example of the C++ principle of dominance.

        In a derived class, a member function with the same name as a member function in the base class but with different arguments is said to dominate. Then, when you try to call the base method, the compiler thinks you mean the derived method and your build fails with wrong arguments errors.
        OK, I never heard of this rule before. Can it be turned off somehow? It's usually great that the compiler thinks for itself, but in this case, I'd rather it didn't. I would like to change the behavior of my setter in my derived class, but not the getter. And users of that class shouldn't need to be aware of how these methods are inherited.

        Also, after looking up dominance, in all cases it said that it only applies to virtual inheritance. Is that true, or somebody just misunderstood the principle?

        Comment

        • weaknessforcats
          Recognized Expert Expert
          • Mar 2007
          • 9214

          #5
          Beeswax. Dominance has nothing to do with virtual inheritance. What is does have to do with is you can't overload functions in different scopes. It is the local scope that dominates.

          It's the same case with a local variable having the same name as a global variable. When you use the name, you get the local variable. The only way to get the global variable is to use the scope resolution operator. That's what the operator is for: To tell the compiler what scope you are using:

          [code=cpp]
          int val = 10;

          void func()
          {
          int val = 0;
          val += 10; //increments the local val
          }

          void funcA()
          {
          int val = 0;
          ::val += 10; //increments the global val
          }
          [/code]




          In the example below, the local Derived::displa y dominates the Base::display.

          This code won't compile:
          [code=cpp]
          class Base
          {
          public:
          void display(int n);
          };
          void Base::display(i nt n)
          {
          cout << n << endl;
          }
          class Derived
          {
          public:
          void display();
          };
          void Derived::displa y()
          {
          cout << 25 << endl;
          }
          int main()
          {
          Derived obj;
          obj.display(10) ;
          }
          [/code]

          It gets this error:

          error C2660: 'Derived::displ ay' : function does not take 1 arguments

          The compiler believes you want to call Derived::displa y.

          Use the scope resolution operator to tell the compiler which scope you want:

          [code=cpp]
          int main()
          {
          Derived obj;
          obj.Base::displ ay(10); //OK
          }
          [/code]

          Comment

          • Harinezumi
            New Member
            • Feb 2008
            • 32

            #6
            OK, I understand what dominance does. What I don't get is why doesn't simple member function inheritance happen here, when it would be easier for the compiler than to guess what I wanted to do?
            In this case the compiler guesses wrong, because I want to distinguish the member functions through their signatures (which I thought was default).

            Comment

            Working...