[Reopened] Why does this error even exist?

Pages: 12
http://coliru.stacked-crooked.com/a/0ef44f6f3df409be
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct Base
{
    virtual ~Base() = default;

    virtual Base *f() const = 0;
};

struct A
: virtual Base
{
    virtual A *f() const override = 0;
};
struct B
: virtual Base
{
    virtual B *f() const override = 0;
};

struct Middle
: virtual A
, virtual B
{
};

struct Derived
: Middle
{
    virtual Derived *f() const override = 0;
};
main.cpp:19:8: error: virtual function 'Base::f' has more than one final overrider in 'Middle'
struct Middle
       ^
main.cpp:5:19: note: overridden virtual function is here
    virtual Base *f() const = 0;
                  ^
main.cpp:11:16: note: final overrider of 'Base::f' in 'A'
    virtual A *f() const override = 0;
               ^
main.cpp:16:16: note: final overrider of 'Base::f' in 'B'
    virtual B *f() const override = 0;
               ^
I don't understand why this error even exists, all the member functions are pure virtual. Middle is actually a template in my code and I can't change it to accommodate for specific cases.

EDIT: I can work around it by just not declaring f() in A or B (it's private anyway) but I would still like to know why this doesn't work.
Last edited on
the problem is return type, it has to be the same in all the classes.

one way to solve this (if you like that approach) is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct Base
{
	virtual ~Base() = default;

	virtual Base *f() = 0;
};

struct A : virtual Base
{
	virtual Base *f() override
	{
		return dynamic_cast<A*>(this);
	}
};

struct B : virtual Base
{
	virtual Base *f() override
	{
		return dynamic_cast<B*>(this);
	}
};

struct Middle : A , B
{
	virtual Base *f() override
	{
		return dynamic_cast<Middle*>(this);
	}
};

struct Derived : Middle
{
	virtual Base *f() override
	{
		return dynamic_cast<Derived*>(this);
	}
};

int main()
{
	// test
	Base* a = new A;
	Base* b = new B;
	Base* c = a->f();
	Base* d = b->f();

	Base* e = new Middle;
	Base* f = e->f();

	// final test
	//get the real object
	Base* g = new Derived;
	Derived* h = dynamic_cast<Derived*>(g->f());
	
	delete a;
	delete b;
	delete e;
	delete g;

	return 0;
}


edit:
also note that you don't have to virtually inherit A and B in Middle class.
Last edited on
You can also virtual Middle *f() const override = 0 in Middle.
You can also virtual Middle *f() const override = 0 in Middle.

true, so my statement above was wrong and final result should then look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct Base
{
	virtual ~Base() = default;

	virtual Base *f() const = 0;
};

struct A : virtual Base
{
	virtual Base *f() const override = 0;
};

struct B : virtual Base
{
	virtual Base *f() const override = 0;
};

struct Middle : A, B
{
	virtual Middle *f() const override = 0;
};

struct Derived : Middle
{
	virtual Derived *f() const override { return new Derived;  }
};

int main()
{
	Derived a;
	Derived* b = a.f();

	delete b;

	return 0;
}
Last edited on
I was actually replying to LB. With that modification, his example compiles.
I was actually replying to LB. With that modification, his example compiles.

I don't think so,
did you try to compile with your modification?
Yes.
- I cannot change Middle
- It does not compile even if all the return types are the same
both examples above compile just fine with msvc-12.0

maybe you're using some ancient compiler? joke lol :)
Microsoft is notorious for accepting non-standard code. It does not compile:
http://coliru.stacked-crooked.com/a/dec09f488b37305a
> I cannot change Middle

Then, any program using Middle would be ill-formed.
1
2
3
4
5
6
struct Middle : virtual A , virtual B
{
      // there is only one Base sub-object in Middle
      // and Miidle has two final overriders of Base::f()
      // A::f() and B::f()
};

In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed. - IS

http://coliru.stacked-crooked.com/a/f1c23517d00036c6
http://rextester.com/FJRK57372
closed account (10X9216C)
You need to do what helios said...

http://coliru.stacked-crooked.com/a/910c8b8ed7dffd27

You need that, if you can't modify middle, or add some sort of dummy:
http://coliru.stacked-crooked.com/a/ab91b0df76f24dbc

Welcome to multiple inheritance.
Last edited on
the code you posted in the link does not compile because you made 0 changes to the 'Middle'
it's doesn't compile with msvc either.

if you wish this to work you'll need to change 'Middle' or redesing your inheritance. virtual keyword will not help.

Anyway I would like to know what's the error output if you do change the Middle ^^

@JLBorges, yes, thanks, but I was hoping more to learn the reasoning behind it.

@myesolar: try to read the first post before responding to a thread.

@codekiddy: try to read the first post before responding to a thread.
@myesolar
I copied your code and it doesn't compile with msvc-120

> but I was hoping more to learn the reasoning behind it.

My guess is that special-casing for all pure virtuals to allow such a construct would place a needless burden on implementations; without giving even a fractionally commensurate benefit to programmers.

In short, it could be done (with the caveat: 'it is up to the client to determine whether they are problem');
but it is probably not worth doing.
http://clang.llvm.org/doxygen/classclang_1_1CXXFinalOverriderMap.html
Ok, I am still not sure why there is even an issue, but there's not much I can do to change anything.
So, bringing back this topic, I really want to work around the issue. This more closely models my actual code:
http://coliru.stacked-crooked.com/a/057ea1a42eaebfb3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
template<typename DerivedT, typename... ParentT>
struct Middle
: virtual ParentT...
{
};

struct Base
: Middle<Base>
{
    virtual ~Base() = default;

    virtual Base *f() const = 0;
};

struct A
: Middle<A, Base>
{
    virtual A *f() const override = 0;
};
struct B
: Middle<B, Base>
{
    virtual B *f() const override = 0;
};

struct Derived
: Middle<Derived, A, B>
{
    virtual Derived *f() const override = 0;
};

int main()
{
}
Still the same error. Middle is a CRTP helper class and it needs to know the parent types, as well as needing to derive them.

This is the solution I came up with:
http://coliru.stacked-crooked.com/a/62162012e871064c
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Resolve
{
};

template<typename... Args>
struct Helper;
template<typename ResolverT, typename... Args>
struct Helper<Resolve, ResolverT, Args...>
: ResolverT::template Inheriter_t<Args...>
{
};
template<typename... Args>
struct Helper
: virtual Args...
{
};

template<typename DerivedT, typename... ParentT>
struct Middle
: Helper<ParentT...>
{
};
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
struct ABResolver
{
    template<typename... Args>
    struct Inheriter_t
    : virtual Args...
    {
        virtual Inheriter_t *f() const override = 0;
    };
};

struct Derived
: Middle<Derived, Resolve, ABResolver, A, B>
{
    virtual Derived *f() const override = 0;
};
It's pretty convoluted and requires extra effort on the user's part, is there a better way of doing it? Ideally the user should just need to provide a class that extends all of the other specified classes and overrides the conflicting member functions as pure virtual.

Note: Middle is huge and has a lot of guts and internals, I can't specialize it without massive code duplication.
Last edited on
> the user should just need to provide a class that extends all of the other specified classes
> and overrides the conflicting member functions as pure virtual.

Ideally, the user should not be required to override a pure virtual as a pure virtual.
Basically: if you have an implementation, give it; otherwise do nothing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template < typename DERIVED, typename... BASE > struct crtp_helper ;

template < typename T > struct crtp_helper<T> { /* ... */ };

struct base : crtp_helper<base>
{
    virtual ~base() = default ;
    const base& get() const { return do_get() ; }
    protected: virtual const base& do_get() const = 0 ;
};

template < typename DERIVED, typename FIRST, typename... REST >
struct crtp_helper< DERIVED, FIRST, REST... > : virtual FIRST, virtual REST...
{
    const DERIVED& get() const { return dynamic_cast< const DERIVED& >( do_get() ) ; }
    protected: virtual const base& do_get() const override = 0 ;
    // ...
};

struct A : crtp_helper<A,base> {}; 
struct B : crtp_helper<B,base> {}; 
struct C : crtp_helper<C,base> {};
struct ABC : crtp_helper<ABC,A,B,C> {};

int main()
{
    struct B1 : B { protected: virtual const base& do_get() const override { return *this ; } };
    struct ABC1 : ABC { protected: virtual const base& do_get() const override { return *this ; } };
    
    B1 b ; const B& rb = b.get() ;
    ABC1 abc ; const ABC& rabc = abc.get() ;;
}

http://coliru.stacked-crooked.com/a/688a8b65494bb60f
I should have specified, the return type for f() in my example should be ignored - it does change in each class but it is not the same as the class it is in. It actually follows a completely different class hierarchy that I omitted for simplicity.
Last edited on
Pages: 12