Re: function defined with base class return derived class object when called with a derived class
On Aug 1, 9:02 pm, SG <s.gesem...@gmail.com> wrote:
On 1 Aug., 00:55, B?RCI Norbert wrote:
I want to do the following:
use_it(B().memA().memB());
I know that it does not work with the definitions above, but I think
if I call memA with a B object, the expression should be considered as
an object of type B.
Why? The function's signature doesn't say anything about what
reference it will be. It could refer to any object and is NOT
restricted to *this.
[...]
There is no semantic problem with this: if I call
memA it will do something with the A part of B. And there is no
syntactic problem either: any code that was correct before, should
compile and run as before: if something was compilable with (B().memA
()) considered as type A it should compile with the same
expression considered as a B also.
Wrong. A::memA() might return a reference to some other object of type
A that is always of type A and not B.
I was talking about a compiler cleverness which is *only* utilized
when a member which returns *this called with the derived class. The
compiler knows that and could fully distinguish it from any other case
(where it could work as currently). So I consider it as a pure
extension.
Lets suppose that class A has a virtual member memv and B (which is
derived from A) has overloaded memv. Considering
B().memA().memv()
is currently evaluated as calling A::memv, but if the complier does
what I explained before, it would call B::memv, so in this case it
behaves differently in existing code. You are right, compiler
cleverness is not good, explicit function signature novelties are
needed.
Worse, there is no *_cast which is able to do the trick, except when I
use a pointer:
use_it( static_cast<B*>(&(B().memA()))->memB() );
Wrong. Try static_cast<B&>
Thank you for a better solution! Yes, it works, but I think it
remained a code bloat (the static_cast did not vanished). Consider I
want to do the following:
use_it(B().memA().memB().memA().memB());
now it becomes:
use_it(static_cast<B&>(static_bast<B&>(B().memA()).memB().memA()).memB
());
So in order to use member functions of a class, I should be aware of
which member belongs to which base classes, so I know when to use
static_cast and when it is not needed (otherwise it becomes more
obscure). I think it is an unnecessary requirement.
But "return *this;" is not part of the function's signature.
That is the point.
auto A::A() { /* ... */ return *this; }
At some point I was actually considering something like this:
class A {
public:
auto foo() -> this;
};
to fix the problem of dangling references in cases like
A const& r = A().foo();
For derived types the return type could be automatically covariant
like you suggested. The return value could even retain its
"valueness" (lvalues -> lvalue, rvalue -> rvalue).
Great!
How about
auto foo( /* parameters */ ) -> decltype( this )
which falls nicely into the current C++0x "new function declarator
syntax" (it currently allows using the ids of the function parameters,
so I see no problem to allow "this" there also).
Do you know what the current C++ draft (or pending submissions) say
about what happens upon derivation? If the compiler must re-evaluate
the decltype expressions, we are ready... :)
More,
auto foo( /* parameters */ ) -> decltype( expression )
could explicitly define not only the return type, but the return
_value_ also if the function definition does not end with a return
statement. We could consider this as a "default" for the return
statement. If the function body has a return statement, current
semantics applied. (Hmmm, I think it would be rare that the decltype
expression is different than the return statement. Isn't it?)
Hmmm, I have just flipped through one of the the newest proposition
(Lawrence Crowl, Alisdair Meredith: Unified function syntax (N2890)),
and it has got the following:
"
[] twice(int x) -> auto { return x+x; }
"
Wow! If we substitue, we got:
[] foo( /* parameters */ ) -> auto { /* do it */ return *this; }
Maybe we are dealing with a problem that is solved already? :D Ok, the
compiler should re-evaulate the member function upon derivation, but I
think it is easy.
The "natural" extension for free functions:
auto flip(-> string io);
{ // ^^
std::reverse(io.begin(),io.end());
}
PROS: Reduces copying & avoids dangling references at the same time
for all functions that can work "in-place".
CONS: Introduces new types of functions that need to be supported
by function pointers and template argument deduction. It
will probably make generic programming horrible. I don't see
how perfect forwarding would be possible.
With the new syntax stated above, is it possible that
[] foo( A & a /* other parameters */ ) -> auto { return a; }
solves it also? (Since it is a new syntax, it won't break any code if
we require that if we return a reference to a type what was passed in
as a paramteter, we should return the object with which we was called
by)
I've come to the conclusion that it's not a good idea to introduce a
whole new class of function types.
I am so interested in the new syntax. I will read through those
propositions...
--
NB
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]