简体   繁体   中英

Overloading conversion operator template

Consider the following simple example

struct C
{
    template <typename T> operator T () {return 0.5;}
    operator int () {return 1;}
    operator bool () {return false;}
};

int main ()
{
    C c;
    double x = c;
    std::cout << x << std::endl;
}

When compiled with Clang, it gives the following error

test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous
    double x = c;
           ^   ~
test.cpp:4:5: note: candidate function
    operator int () {return 1;}
    ^
test.cpp:5:5: note: candidate function
    operator bool () {return false;}
    ^
test.cpp:3:27: note: candidate function [with T = double]
    template <typename T> operator T () {return 0.5;}
                          ^
1 error generated.

Other compilers generate similar errors, eg, GCC and Intel iclc

If I remove operator int and operator bool . It compiles fine and work as expected. If only remove one of them, that is keep the template operator and say operator int , then the non-template version is always chosen.

My understanding is that only when the template and non-template overloaded functions are equal in the sense that they are both perfect match or both require the same conversion sequence, the non-template version will be preferred. However in this case, it appears that the compiler does not see the operator template as a perfect match. And when both the bool and int overloading are present, then naturally it considers they are ambiguous.

In summary, my question is that why the operator template is not considered a perfect match in this case?

Let's break this down into two different problems:

1. Why does this generate a compiler error?

struct C
{
    operator bool () {return false;}
    operator int () {return 1;}
};

As both int and bool can be implicitly converted to double , the compiler can not know which function it should use. There are two functions which it could use and neither one takes precedence over the other one.

2. Why isn't the templated version a perfect match?

struct C
{
    template <typename T> operator T () {return 0.5;}
    operator int () {return 1;}
};

Why is operator int() called when requesting a double?

The non-template function is called because a non-template function takes precedence in overload resolution. ( Overloading function templates )

EDIT: I was wrong! As Yan Zhou mentioned in his comment, and as it is stated in the link I provided, a perfect match in the templated function takes precedence over the non-templated function.

I tested your code (compiled with g++ 4.7.2), and it worked as expected: it returned 0.5 , in other words, the templated function was used!

EDIT2: I now tried with clang and I can reproduce the behaviour you described. As it works correctly in gcc, this seems to be a bug in clang.

This is interesting. There are two ways to read a critical part of section 13.3.3. The original example should definitely call the function template, but the version where one of the non-templates is removed might be argued to be ambiguous.

13.3.3:

A viable function F1 is defined to be a better function than another viable function F2 if for all arguments i , ICS_i( F1 ) is not a worse conversion sequence than ICS_i( F2 ), and then

  • for some argument j , ICS_j( F1 ) is a better conversion sequence than ICS_j( F2 ), or, if not that,

  • the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (ie, the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type, or, if not that,

  • F1 is a non-template function and F2 is a function template specialization, or, if not that,

  • F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.

In the example, clang correctly identifies the set of three viable candidate functions:

C::operator int()
C::operator bool()
C::operator double<double>()

The third is a function template specialization. (I don't think the syntax above is legal, but you get the idea: at this point of overload resolution it's not treated as a template, but as a specialization with a definite function type.)

The only Implicit Conversion Sequence on arguments here (ICS1) is the exact match "lvalue C " to " C& " on the implicit parameter, so that won't make a difference.

This example is exactly the situation described in the second bullet, so the function returning double is clearly better than the other two.

Here's where it gets weird: By a very literal reading, operator int is also better than the template specialization, because of the third bullet. "Wait a minute, shouldn't 'better than' be antisymmetric? How can you say F1 is better than F2 AND F2 is better than F1 ?" Unfortunately, the Standard doesn't explicitly say anything of the sort. "Doesn't the second bullet take priority over the third bullet because of the 'if not that' phrase?" Yes, for constant F1 and F2 . But the Standard doesn't say that satisfying the second bullet for (F1,F2) makes the third bullet for (F2,F1) not applicable.

Of course, since operator int is not better than operator bool and vice versa, there is still "exactly one viable function that is a better function than all other viable functions".

I'm not exactly endorsing this weird reading, except maybe to report it as a Standard defect. Going with that would have bizarre consequences (like removing an overload which was not the best from this example changes a program from well-formed to ambiguous!). I think the intent is for the second bullet to be considered both ways before the third bullet is considered at all.

Which would mean the function template should be selected by overload resolution, and this is a clang bug.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM