struct X { };
struct Y : X { };
struct A
{
virtual void X* foo();
virtual void X& bar();
};
struct B : A
{
virtual void Y* foo() override;
virtual void Y& bar() override;
};
On 2014–12–23, at 10:00 PM, Daniel Gutson <daniel...@gmail.com> wrote:In a past post, I wanted to go further by providing some mechanism to allow conversion from const vector<Derived>& to const vector<Base>& (vector being an example). However. This somehow overlaps the domain of concepts and parametric polymorphism.
What do you think?
struct A
{
void X* foo() { return __foo(); }
void X& bar() { return __bar(); }
virtual X* __foo();
virtual X& __bar();
};
struct B : A
{
virtual X* __foo() override;
virtual X& __bar() override;
Y* foo() { return static_cast<Y*>(this->foo()); }
X& bar() { return static_cast<Y&>(this->bar()); }
};
My bad, errata: I meant pointers:
Conversion from
const vector<Derived*>&
to
const vector<Base*>&
AFAIK C# has an equivalent facility.
On 2014–12–23, at 10:31 PM, Jean-Marc Bourguet <jm.bo...@gmail.com> wrote:Your example is compiled as
I see where you are coming from. There are details that have to be considered to be sure that it is implementable with a correct semantic. The issue I see is that with implementation technique I know of covariant return, not only T has to be implicitly convertible to U but from a U you need to be able to reconstruct the T. Your example is compiled as
struct A
{
void X* foo() { return __foo(); }
void X& bar() { return __bar(); }
virtual X* __foo();
virtual X& __bar();
};
struct B : A
{
virtual X* __foo() override;
virtual X& __bar() override;
Y* foo() { return static_cast<Y*>(this->foo()); }
X& bar() { return static_cast<Y&>(this->bar()); }
};implicit conversion don't guarantee this round trip. You will at least to show that there is a way to compile with the semantic you want.
struct A
{
void U foo() { return __foo(); }
virtual U __foo();
};
struct B : A
{
virtual U __foo() override { return U{foo()}; }
T foo();
};
The idea has been floating around for a while. Are there open questions remaining? Has there been any formal write-up?
I think you based your request on the simplest example possible. Yes, it would
work for your example, but not for anything a little more complex.
struct X {};
struct Spacing { char buffer[100]; }
struct Y: Spacing, X {};
Now the covariant returns are permitted, but require a pointer-adjustment
thunk because the value of an X* differs from an Y* if they point to the same
object.
With that in mind, your request implies:
- alll smart pointers must be able to handle deleting of derived classes if
they delete, which isn't the case (example: std::unique_ptr with default
deleter)
- all smart pointers must be able to handle copying from a derived class
pointer or object containing the derived class
- the compiler must know how to copy
The first and last requirements are a non-starter.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
If that is permitted, what's stopping one from writing:
struct A
{
virtual Foo foo();
};
struct B : A
{
virtual Bar foo() override;
};
for two unrelated types Foo and Bar, provided that
Bar b{Foo()};
compiles?
On Tuesday 23 December 2014 11:31:58 Andy Prowl wrote:
> Nothing. What I'm proposing is to take into consideration *any* viable
> implicit conversions, including user-defined ones; the conversions from
> unique_ptr<D> to unique_ptr<B>, from shared_ptr<D> to shared_ptr<B>, from
> D* to B*, from D& to B&, etc. would be just particular cases of the general
> rule.
Why do a covariant override at all?
In my experience, most covariant overrides can be replaced by a non-covariant
override returning the original type and adjust the caller code to up-cast as
needed.
> I can't find a good reason why your example above should not compile. If
> "Bar b{Foo()};" (or rather, "Bar b = Foo();") compiles, this means a Foo
> can be provided wherever a Bar is expected: in that case, why should we
> prevent an overriding function to return a Foo when the caller of the
> overridden function is expecting a Bar?
Data loss. Foo and Bar may be copyable, but may not be supersets of one
another.
Given that same rule, then:
struct A
{
virtual unsigned foo();
};
struct B : A
{
unsigned long long x;
virtual unsigned long long foo() override
{ return x; }
};
The above also compiles and you can virtually call foo() on A, getting a
truncated value without any warnings.
unsigned long long x = ...;
unsigned y = x;
Although, Thiago's example may or may not be legal depending on whether
the conversion is defined as construction (i.e. brace initialization) or
a static_cast. The former is actually ill-formed for the above and
should error in this case; i.e. covariant returns would require a user
defined conversion operator or constructor.
Please tell me where the compiler would print a warning here:
struct A
{
virtual unsigned foo();
}
void f(A *a)
{
a->foo();
}
Because that's all it sees at the call site.
Or are you saying that it should generate a warning on the following, without
the definition of the function?
struct B: A
{
virtual unsigned long long foo() override;
};
"warning: conversion from `unsigned long long' to `unsigned' may alter its
value in the return of virtual override of foo()"
> unsigned long long x = ...;
> unsigned y = x;
>
> If I am writing the above, it means I know what I am doing. If the data
> loss matters, then I'm doing the wrong thing, yet nobody is stopping me.
> Why should we try to stop the user from doing the same in the context of
> covariant return types?
Because in the above, there was an explicit assignment of a larger integer to
a smaller one, even if the cast itself was implicit.
unsigned f(unsigned long long y) { return y; }
=> warning: conversion to ‘unsigned int’ from ‘long long unsigned int’ may
alter its value [-Wconversion]
But I can silence such a warning with:
unsigned f(unsigned long long y) { return unsigned(y); }'
How do you suggest modifying the definition of B above so the compiler knows
not to emit a warning?
Either use a pragma, or don't use a covariant return that results in a
warning in the first place :-).
In practice I would expect covariant returns of integral types to be
much less common than of [pointers to] class types.
If we *really* need this, I almost feel like the correct solution is to
allow the user to supply definitions of both the base override and the
flavor with the covariant return type. (Which, yeah, violates the 'no
overload on return type' rule... we'd need a careful exception.)
(I don't think we had the necessary introspection to be *able* to
diagnose this as of C++11, which probably has something to do with why
it isn't being diagnosed... At the very least, this should trip some
warning, e.g. -Wnon-virtual-dtor.)