Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implications of conversion function template argument deduction in C++ (stackoverflow cross-post)

23 views
Skip to first unread message

David Hollman

unread,
Sep 1, 2016, 11:56:55 AM9/1/16
to
Hi all,

I'm cross-posting this stackoverflow question here because I think someone on this list is most likely to have the answer:

http://stackoverflow.com/questions/39150553/implications-of-conversion-function-template-argument-deduction-in-c

Thanks!

David Hollman, Ph.D.

Victor Bazarov

unread,
Sep 1, 2016, 12:49:49 PM9/1/16
to
If you want an answer here, you need to post the actual question instead
of just a link. See FAQ, section 5.

V
--
I do not respond to top-posted replies, please don't ask

jacob navia

unread,
Sep 1, 2016, 3:25:37 PM9/1/16
to
Le 01/09/2016 à 18:49, Victor Bazarov a écrit :
> On 9/1/2016 11:56 AM, David Hollman wrote:
>> Hi all,
>>
>> I'm cross-posting this stackoverflow question here because I think
>> someone on this list is most likely to have the answer:
>>
>> http://stackoverflow.com/questions/39150553/implications-of-conversion-function-template-argument-deduction-in-c
>>
>>
>> Thanks!
>>
>> David Hollman, Ph.D.
>
> If you want an answer here, you need to post the actual question instead
> of just a link. See FAQ, section 5.
>
> V

Mmmmm

What would be the point of posting all that text here? A link is a
better solution.

Anyway:

<quote>
I'm having trouble understanding the implications of the conversion
function template argument deduction rules in the C++ standard. The
standard states that ([temp.deduct.conv] clause 1, §14.8.2.3.1 in N4594):

Template argument deduction is done by comparing the return type of the
conversion function template (call it P) with the type that is required
as the result of the conversion (call it A; see 8.5, 13.3.1.5, and
13.3.1.6 for the determination of that type) as described in 14.8.2.5.
where 14.8.2.5 ([temp.deduct.type]) is the section that describes
general template argument deduction (though the most common case,
function call template argument deduction [temp.deduct.call], no longer
seems to point there; did it ever?). The next clause is what confuses
me, though (clause 2):

If P is a reference type, the type referred to by P is used in place of
P for type deduction and for any further references to or
transformations of P in the remainder of this section.
To me, this seems to imply that template <class T> operator T() and
template <class T> operator T&() are the same (and specifying both would
result in an ambiguity). But that isn't the case in any compiler I've
used! For instance:

struct any1 { template <typename T> operator T() { } };

struct any2 { template <typename T> operator T&() { } };

void f1(int) { }
void f2(int&) { }
void f3(int const&) { }

int main() {
f1(any1());
// f2(any1()); compile time error
f3(any1());

f1(any2());
f2(any2());
f3(any2());
}
------------------------------------------------------------<end quote>

When I compile that code above I do not get any compile error.

Then, I move the commented out code back in, and compile again.

I obtain:

~/tmp $ gcc cpp1.cpp
cpp1.cpp:11:3: error: no matching function for call to 'f2'
f2(any1()); //compile time error
^~
cpp1.cpp:6:6: note: candidate function not viable: no known conversion
from 'any1' to 'int &' for 1st argument
void f2(int&) { }
^
1 error generated.
~/tmp $

Excellent!
That's exactly what I thought when reading the code above. Where is the
conversion from that structure to an int?

By the way the compiler is:
~/tmp $ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr
--with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
~/tmp $

OK, I think the example is not quite finished. It should contain a
conversion to int, starting with the given structure. By the way clang
notices that and emits warnings.

But let's assume the conversion is done somewhere and continue with the
actual discussion.

<quote------------------------------------------------------------->
But if references are ignored, any1 and any2 should have the same
behavior, right? Clearly they don't, since f2(any1()) doesn't compile
with either gcc or clang, while f2(any2()) compiles fine with both.

The next clause (clause 3, particularly 3.3) confuses things even further:

If A is not a reference type: [...] If P is a cv-qualified type, the top
level cv-qualifiers of P’s type are ignored for type deduction.
This, along with clause 2 about the removal of references, would seem to
imply that the following code should not compile because of an ambiguity:

struct any3 {
template <typename T> operator T&() { }
template <typename T> operator T const&() { }
};

void f1(int) { }

int main() {
f1(any3());
}
Live Demo

And yet this works fine with both gcc and clang.

What am I missing?

Edit

I should clarify that the way the clang and gcc compilers handle this is
exactly what I would expect from a general (relatively advanced)
understanding of C++. Some commenters have asked for clarification on
what my confusion is (and, implicitly, why I should care). My confusion
here is entirely related to trying to understand the implications of the
standard. I need a clear understanding of this because I am submitting a
paper with code that relies heavily on this working and on my use of it
being standards-compliant.
<-------------------------------------------------------------end quote>




Öö Tiib

unread,
Sep 2, 2016, 7:45:28 AM9/2/16
to
Your question seems to be that misunderstanding:

...
| The next clause is what confuses me, though (clause 2):
|
| If P is a reference type, the type referred to by P is used in place of
| P for type deduction and for any further references to or
| transformations of P in the remainder of this section.
| To me, this seems to imply that template <class T> operator T() and
| template <class T> operator T&() are the same (and specifying both would
| result in an ambiguity). But that isn't the case in any compiler I've
| used!
...
| What I'm missing?

What standard said was exactly opposite IMHO. Implementation
removes reference from type that it deduced. Therefore if you want
conversion to reference then you have to type it like
"<class T> operator T&()" because "<class T> operator T()" does not
return references (since reference was removed even if it deduced
reference). Similarly if you want the operator to return value
then you have to type "<class T> operator T()" because
"<class T> operator T&()" does not return values. How you read direct
opposite out of that normative text?

Also better join the club that never writes nor uses any implicit
conversion operators or constructors and never uses C style casts.
Behave like those evil things are missing from C++.

People who behave like that start to see the implicit ugliness of
their code because the conversions become explicit and ugly. Also
they become lazy to convert because the conversions become verbose
to type. Result is better thought through, easier to understand, has
better efficiency, contains less conversion errors and so on. ;)
May be there are few extremely diligent persons with no aesthetic
sense with whom it does not work but those should not work as
programmers anyway.

jacob navia

unread,
Sep 2, 2016, 3:38:52 PM9/2/16
to
Le 02/09/2016 à 13:44, Öö Tiib a écrit :
> Implementation
> removes reference from type that it deduced. Therefore if you want
> conversion to reference then you have to type it like
> "<class T> operator T&()" because "<class T> operator T()" does not
> return references (since reference was removed even if it deduced
> reference). Similarly if you want the operator to return value
> then you have to type "<class T> operator T()" because
> "<class T> operator T&()" does not return values.

The implementation removes the references in the arguments but not on
the return value. The return value is specified as a reference, not as a
value, and THAT reference is NOT removed.

Do I understand correctly?

This provokes an error in the second line.

This asymetric behavior is somehow surprising.

Gareth Owen

unread,
Sep 3, 2016, 2:27:34 AM9/3/16
to
jacob navia <ja...@jacob.remcomp.fr> writes:

> This provokes an error in the second line.
>
> This asymetric behavior is somehow surprising.

Isn't the difference that you return a temporary object, and then
attempt to to pass a non-const reference to that temporary?
And in the other you have a legitimate non-const reference?

jacobnavia

unread,
Sep 3, 2016, 4:02:20 AM9/3/16
to
Well, shouldn't any references be ignored also in the return value of
the template and the template made to return the temporary object and
note a reference to it?

But if we would do that we would forbid returning a reference at all
from a template what would have another (surely worst) implications.


Gareth Owen

unread,
Sep 3, 2016, 11:45:57 AM9/3/16
to
jacobnavia <ja...@jacob.remcomp.fr> writes:

> Le 03/09/2016 à 08:27, Gareth Owen a écrit :
>> jacob navia <ja...@jacob.remcomp.fr> writes:
>>
>>> This provokes an error in the second line.
>>>
>>> This asymetric behavior is somehow surprising.
>>
>> Isn't the difference that you return a temporary object, and then
>> attempt to to pass a non-const reference to that temporary?
>> And in the other you have a legitimate non-const reference?
>>
>
> Well, shouldn't any references be ignored also in the return value of
> the template and the template made to return the temporary object and
> note a reference to it?

I don't see anything in the description that suggests. The reference is
only removed for the purpose of type deduction.

0 new messages