7

Possible Duplicate:
Inherit interfaces which share a method name

I have two base classes I1 and I2 with pure virtual functions void R() = 0;. I want the derived class IImpl to inherit from I1 and I2 and to have distinct implementations for I1::R() and I2::R().

The code below compiles and works in MS VS 2005 and 2010. I compile with Language Extension disabled and on warning level 4. There are no warnings and no errors.

I tried the same code in gcc 4.2. It does not compile. GCC reports an error:

error: cannot define member function 'I1::R' within 'IImpl'

My questions are:

  1. Why that code works in MS VS and why it does not work in gcc?
  2. Is the code a standard C++?
  3. What is the correct way to implement it, so it is a standard C++ and compiles on VS and gcc?

Thanks!

#include <stdio.h>

class I1
{
public:
    virtual void R() = 0;
    virtual ~I1(){}
};

class I2
{
public:
    virtual void R() = 0;
    virtual ~I2(){}
};

class IImpl: public I1, public I2
{
public:

    virtual void I1::R()
    {
        printf("I1::R()\r\n");
    }

    virtual void I2::R()
    {
        printf("I2::R()\r\n");
    }
};

int main(int argc, char* argv[])
{
    IImpl impl;

    I1 *p1 = &impl;
    I2 *p2 = &impl;

    p1->R();
    p2->R();

    return 0;
}
Community
  • 1
  • 1
user1017802
  • 71
  • 1
  • 3

4 Answers4

7

This code is non-standard, according to 8.3/1

A declarator-id shall not be qualified except for the definition of a member function (9.3) or static data member (9.4) outside of its class,...

so you can't qualify member function names when declaring/defining them inside the class. That's why it isn't compiled by gcc.

MSVC seems to have a non-standard extension allowing such code. I guess it was done for Managed Extensions for C++, that's exactly how explicit interface implementations were done there and although it was deprecated long ago seems that syntax is still supported.

A correct way to implent it is decribed at the link provided by Björn Pollex.

Community
  • 1
  • 1
Konstantin Oznobihin
  • 5,234
  • 24
  • 31
2

You only get to implement void R() once in IImpl.

ObscureRobot
  • 7,306
  • 2
  • 27
  • 36
2
IImpl impl;

I1 *p1 = &impl;
I2 *p2 = &impl;

p1->R();
p2->R();

This doesn't make sense to me. R() is a virtual method, so it doesn't matter whether you call it on I1* or I2* as long as the real object is IImpl. Besides, IImpl has two similar R(), which one do you expect to be called for impl.R()? I'm not familiar with the standard enough to quote it, but I'm sure what you're doing is an error. Correct me if I'm wrong.

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • 5
    Although it's a non-standard code it still makes sense. It works just like explicit interface implementations in .NET. And looking under the hood both I1 and I2 have pointer to virtual functions table, and it's not hard to imagine these pointers to point to different tables resulting in different methods to be called. Still it's an error in C++ to do so. – Konstantin Oznobihin Oct 28 '11 at 07:41
  • 1
    The syntax is non-standard, but the underlying concept is perfectly sound. – curiousguy Oct 28 '11 at 12:18
  • 1
    Please read my [answer](http://stackoverflow.com/questions/7925792/distinct-implementations-for-pure-virtual-functions-with-same-name/7937358#7937358). – curiousguy Nov 01 '11 at 00:30
2

@Violet Giraffe

This is an answer to Violet Giraffe's answer. It also clears some misunderstandings of late binding and MI, which is more flexible than Java inheritance.

so it doesn't matter whether you call it on I1* or I2* as long as the real object is IImpl.

// interfaces
class I1
{
public:
    virtual void f () = 0;
};

class I2
{
public:
    virtual void f () = 0;
};

// concrete implementations
class C1 : public I1
{
public:
    virtual void f () { cout << "C1::f()\n"; }
};

class C2 : public I2
{
public:
    virtual void f () { cout << "C2::f()\n"; }
};

// putting both together
class D : public C1, public C2
{
};

template <class I>
void f (I &i) {
    i.f(); // virtual call to I::f()
}

int main() {
    D d;
    f<I1> (d); // virtual call to I1::f(): prints C1::f()
    f<I2> (d); // virtual call to I2::f(): prints C2::f()
}

A call to a member function is always statically bound to one function declaration (here I1::f() and I2::f()). At runtime, the function called (by the process called "late binding") is the most derived overrider (technical term is "final") of this virtual function (here C1::f() and C2::f()).

In any class, there must be at most one final overrider of every pure virtual function. In a non-abstract class, there must be exactly one final overrider of every virtual function.

Now you see that I1::f() and I2::f() are different functions, and that they are quite unrelated; but both have the same unqualified name and same parameters, so they cannot be distinguished by overloading. And if the qualified name is used (d.I1::f() and d.I2::f()) the call would bind to the correct declaration, but virtual-ness would also be inhibited (in this case a definition of the pure virtual function must exist and will be called). So the only way to call either function is what I have done here: first make a lvalue of type I1/I2, only then make an unqualified call .f().

Side note: This kind of distinct overriding is impossible in Java, because MI is strictly limited, and overriding follows different rules.

Besides, IImpl has two similar R(), which one do you expect to be called for impl.R()?

Which f() do you expect to be called in d.f()?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • First of all, thank you for your detailed comment, it was really educating. It's a pity I can't cast more than one vote for this post. The template trick is quite neat. Now, as for calling `d.f()`: I expected to get compile error, something about ambiguous call. I'm quite surprised that the error is `'f': identifier not found` (MSVC 2010). How so? – Violet Giraffe Nov 01 '11 at 19:18
  • The error message is not very helpful. (I have seen many example of very strange messages from many different compilers, but MSVC is among the weirdest.) The "identifier not found" error may be related to the fact that the issue here is not that overloading cannot select one best match, as overloading does not even occur: here **name lookup cannot select a set of candidate overloaded functions**. This is because overloading can work with functions in different scopes, but not if they are ordinary member functions. Here name lookup finds two `f()` members in different classes. – curiousguy Nov 02 '11 at 22:01