struct Foo
{
operator const char* const&() const
{
return s; // <-- "warning: returning reference to temporary"
}
operator char*&()
{
return s; // <-- no warning!
}
char* s;
};
Aside from the "why would you want to do that anyway?" (there is a
reason!), could someone please explain the warning, and why there is
no warning on the non-const version?
Should there be a warning on the non-const version, or is it actually
OK?
Thanks!
-Adam
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
's' is a 'char*' or, in this function, a 'char*const'. Converting it to
a 'char const*const' involves creation of a temporary.
> Should there be a warning on the non-const version, or is it actually
> OK?
The non-const version doesn't involve the creation of a temporary, hence it
is okay. Actually, you could also switch the const one to not doing that,
but that would mean changing the return type to 'char* const&'.
Uli
I think that the const version has to implicitly convert the (char*) to
a temporary (const char*) and the returns a const reference to this.
This is probably not what you want.
Either the signature: operator char* const&() const;
Or: operator const char*() const;
> Should there be a warning on the non-const version, or is it actually
> OK?
>
This is certainly OK.
Because `const char*' is a pointer to const char, and Foo::s is a
pointer to non-const char: compiler automatically casts pointer to non-
const to const, so here's a temporary. The following is equivalent of
code generated by a typical compiler:
operator const char* const&() const
{
const char *tmp = s;
return tmp;
}
With non-const vesion types already match, so there's no need in
conversion, hence no temporary is created.
You might want to remove reference symbol from operator prototype.
--
Cheers,
Alex
I admit it is rather cryptic. I suppose the compiler creates a temporary
string pointer and since you are returning a reference to it; it warns
you about temporaries.
As to why, I don't known. I tried with const_casts but without result.
> and why there is
> no warning on the non-const version?
Because it is not the same: in the first one, the content of the string
is const, in the second, it is not.
> Should there be a warning on the non-const version, or is it actually
> OK?
The non-const version is OK.
The const version should be:
operator char* const&() const
{
return s;
}
If the semantic you want is that the caller can modify the string but
not the pointer in Foo.
Or:
operator char const * const() const
{
return s;
}
If the semantic you want is that the caller cannot modify the string.
--
Michael
I suppose, that in the first function compiler creates temporary:
'const char* tmp = s' and later returns reference to it. That's reason
for warning. In the second function it's only returning reference to
s. That's why there is no warning.
Regards,
Mateusz
Just lose the "&":
operator const char * const () const
... and you're ok.
Compiler is doing something like this:
const char * const & res = s;
return res;
... and there is no direct binding of T2 to a reference to T1,
where T1 != T2 and T1,T2 are pointers defined in the standard.
I don't know of the _correct_ reason for this, but it's not
a problem since you can return a pointer by its value.
--
Best regards,
Dragan
irotas wrote:
> Consider the following code:
>
> struct Foo
> {
>
> operator const char* const&() const
> {
> return s; // <-- "warning: returning reference to temporary"
> }
>
> operator char*&()
> {
> return s; // <-- no warning!
> }
>
> char* s;
> };
>
>
> Aside from the "why would you want to do that anyway?" (there is a
> reason!), could someone please explain the warning, and why there is
> no warning on the non-const version?
>
> Should there be a warning on the non-const version, or is it actually
> OK?
The warning is correct, because the type of "s" seen in the
const-qualified method is "char* const" as opposed to "char const*". So
what happens is an implicit conversion of a value of type "char*" to a
value of type "char const*" which creates a temporary. After that a
constant reference to that temporary is obtained and returned.
In the non const-qualified version no conversion takes place, and
therefor a reference to the member variable itself is returned.
The reason why the conversion takes place is more obvious in the
following example where I simply introduced alias names for the types in
question:
struct Foo
{
typedef char* pointer;
typedef char const* const_pointer;
operator const_pointer const&() const
{
// Type of s is "pointer const" here
return s; // <-- "warning: returning reference to temporary"
}
operator pointer&()
{
return s; // <-- no warning!
}
pointer s;
};
You have two options to avoid this warning:
1. Change the signature to:
operator pointer const&() const
(Return const reference to member)
2. Change the signature to:
operator const_pointer() const
(Return by value with implicit conversion)
Which one you chose depends on your use case (which you specifically
requested not to be asked about).
Best regards,
Kimon
The non-const version returns a reference to the actual type of the
member variable. That must be OK!
The const version returns a different type, and so possibly involves a
conversion - creating a temporary.
FWIW, the Comeau compiler gives the same warning, and it is usually
correct on language issues.
It is not at all obvious why anyone would return a reference to a
const pointer to a const object. :-)
Bo Persson
I'm confused, since as far as I understand, returning a pointer rather
than reference WOULD work (4.4 para 4), without creating any
temporaries:
struct Foo
{
char* s;
operator const char* const*() const { return &s; } // OK, right?
operator const char* const&() const { return s; } // why not OK?
};
If the reference version really is invalid (and the pointer version
not),
then this is one glaring difference between references and pointers
where
one would expect them to behave the same.
Well, references and pointers are different. :-)
In the pointer case, you actually return the pointer BY VALUE. That
way you get a temporary anyway, but it is copied, so it doesn't matter.
The reference version returns a reference to a temporary that
immediately goes out of scope. That's serious!
Bo Persson
I don't think that will compile. You probably meant:
operator const char* const() const { return &s; }
> operator const char* const&() const { return s; } // why not OK?
Because your member is of type [char*]. You are casting from [char*] to
[const char*] and that is a different type. Note that:
operator char* const() const& { return s; }
should work.
> };
>
> If the reference version really is invalid (and the pointer version
> not),
> then this is one glaring difference between references and pointers
> where
> one would expect them to behave the same.
>
Well. As you a dealing with a [pointer to char] you should compare it
with a [reference to char] and NOT, as you do in the above example, with
a [reference to pointer to char].
br,
Martin
I should have stated that this is not a beginner question, thus it
warrants close attention to details. :)
> In the pointer case, you actually return the pointer BY VALUE. That
> way you get a temporary anyway, but it is copied, so it doesn't
> matter.
>
> The reference version returns a reference to a temporary that
> immediately goes out of scope. That's serious!
Let me add a third function that illustrates my point:
struct Foo
{
char* s;
operator const char* const*() const { return &s; } // OK
operator const char* const&() const { return s; } // not OK
// you're saying this is what the above does:
operator const char* const&() const
{
const char* const p = s;
return p; // NOT OK, returning reference to local
}
// I'm saying, why doesn't it instead do this:
operator const char* const&() const
{
const char* const* p = &s; // OK
return *p; // OK, reference to original s object
}
};
The last operator function shows that a temporary isn't necessary in
this
case (just as it's not necessary in the pointer case).
Why not? &s has type 'char* const*', which is convertible to 'const
char* const*' (C++03 section 4.4 paragraph 4).
> > operator const char* const&() const { return s; } // why not
> > OK?
>
> Because your member is of type [char*]. You are casting from [char*]
> to
> [const char*] and that is a different type. Note that:
> operator char* const() const& { return s; }
> should work.
I'm assuming you mean
operator char* const& () const { return s; }
Sure, this will work, but it eliminates the interesting aspect.
Since the equivalent pointer version is OK, why shouldn't I be able to
take a reference of type 'const char* const&' to an object of type
'char* const'? I cannot change the pointer through the reference (just
as with the equivalent pointer version), so there's no need for a
temporary. Put another way, if there needed to be a temporary, then
the equivalent pointer version would also need a temporary.
> > };
> >
> > If the reference version really is invalid (and the pointer version
> > not), then this is one glaring difference between references and
> > pointers where one would expect them to behave the same.
>
> Well. As you a dealing with a [pointer to char] you should compare it
> with a [reference to char] and NOT, as you do in the above example,
> with
> a [reference to pointer to char].
Let's generalize that, for clarity: "As you a dealing with a [pointer
to T] you should compare it with a [reference to T] and NOT, as you do
in the above example, with a [reference to pointer to T]."
Above, I AM doing the first, where T is 'const pointer to const char':
struct Foo
{
char* s;
typedef const char* const T;
operator T* () const { return &s; }
operator T& () const { return s; }
};
Doesn't it seem odd that the operator T* above is valid, but operator
T& is not (at least according to a few compilers and posters here)?
I'm sorry you're right. I was just not able to parse the syntax :-)
> (...)
>
> Since the equivalent pointer version is OK, why shouldn't I be able to
> take a reference of type 'const char* const&' to an object of type
> 'char* const'? I cannot change the pointer through the reference (just
> as with the equivalent pointer version), so there's no need for a
> temporary. Put another way, if there needed to be a temporary, then
> the equivalent pointer version would also need a temporary.
>
You are quite right I think - see below.
>>> };
>>>
>>> If the reference version really is invalid (and the pointer version
>>> not), then this is one glaring difference between references and
>>> pointers where one would expect them to behave the same.
>> Well. As you a dealing with a [pointer to char] you should compare it
>> with a [reference to char] and NOT, as you do in the above example,
>> with
>> a [reference to pointer to char].
>
> Let's generalize that, for clarity: "As you a dealing with a [pointer
> to T] you should compare it with a [reference to T] and NOT, as you do
> in the above example, with a [reference to pointer to T]."
>
> Above, I AM doing the first, where T is 'const pointer to const char':
Yes, I misread what you intended to do.
>
> struct Foo
> {
> char* s;
>
> typedef const char* const T;
>
> operator T* () const { return &s; }
> operator T& () const { return s; }
> };
>
> Doesn't it seem odd that the operator T* above is valid, but operator
> T& is not (at least according to a few compilers and posters here)?
>
I have tried to compile your example with Visual Studio 2005 (language
extensions OFF and warning level 4). It compiles without warnings:
#############
struct Foo
{
char* s;
typedef const char* const T;
// Both Versions work with MS VC8 (VS 2005) with
// Language extensions disabled and
// Warning level 4.
// (Indeed, the compiler generates exactly the same assembly code for
both.)
operator T* () const { return &s; }
operator T& () const { return s; }
};
int main(int, char**)
{
Foo f;
const char*const* xf1 = f; //.operator const char *const *();
const char*const& xf2 = f; //.operator const char *const &();
xf1;
xf2;
return 0;
}
#############
I have tried it with comeau online and this gives:
"ComeauTest.c", line 12: warning: returning reference to local temporary
operator T& () const { return s; }
So it would be quite interesting to see if comeau or gcc actually
produces different code for the two operators ...
cheers,
Martin
(In case I'm talking nonsense below, it's because my head feels
like 10kg)
In the first case, it is a valid conversion. In the second case,
we are not talking about conversions, but about binding a value to
a reference. So this is a big difference between pointers
and references.
I've said in my other post:
> there is no direct binding of T2 to a reference to T1,
> where T1 != T2 and T1,T2 are pointers
So, the standard doesn't allow mentioned binding. Sure, there is
a conversion between pointers, but conversions create temporaries.
Anyway, I don't know the reason behind such a decision,
maybe someone else could help with that.
I've seen your other post which shows how to "manually" provide
such a reference, and I guess it proves that pointers too should
be bindable (although I didn't consider other consequences of such
a change).
> // I'm saying, why doesn't it instead do this:
> operator const char* const&() const
> {
> const char* const* p = &s; // OK
> return *p; // OK, reference to original s object
> }
--
Best regards,
Dragan