Overloading conversion operator template - c++

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, e.g., 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 (i.e., 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.

Related

templated operator "+" overloading and enum initialization [constexpr][msvc]

Please, consider the following code:
template<typename T>
T operator+(const T& lvh, const T& rvh)
{
return lvh;
}
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
int main() {
return 0;
}
Compiling with MS VS 2022 gives the following error:
error C2131: expression did not evaluate to a constant
message : failure was caused by call of undefined function or one not declared 'constexpr'
message : see usage of 'operator +'
The problem applies to the third element in enum.
Why cl tries to substitute template for the constant expression?
Compilation with gcc gives no errors.
I can eliminate the error with cl adding type specifier to the enum, like this:
enum : int {
...
}
But I can`t do this in the project I work with. Both enum and overload declared by 3rd party headers. All I can do is to change includ order. But I'm still wondering why it works that way...
To me it looks like MSVC is correct here.
First, because template argument deduction should happen before overload resolution to form a set of candidate functions:
Before overload resolution begins, the functions selected by name
lookup and template argument deduction are combined to form the set of
candidate functions
And then, since the non-member candidates (in this case template specialisation of the operator+ overload with the enum_container::<anonymous_enum> parameters) precede built-in candidates (conversion to the underlying built-in type, for which a user-defined overload would not be possible), the compiler is meant to resolve the overload to the instance of the template with the user-defined enum-type:
non-member candidates: For the operators where operator overloading
permits non-member forms, all declarations found by unqualified name
lookup of operator# in the context of the expression (which may
involve ADL), except that member function declarations are ignored and
do not prevent the lookup from continuing into the next enclosing
scope. If both operands of a binary operator or the only operand of a
unary operator has enumeration type, the only functions from the
lookup set that become non-member candidates are the ones whose
parameter has that enumeration type (or reference to that enumeration
type)
built-in candidates: For operator,, the unary operator&, and the operator->, the set of built-in candidates is empty. For other
operators built-in candidates are the ones listed in built-in operator
pages as long as all operands can be implicitly converted to their
parameters. If any built-in candidate has the same parameter list as a
non-member candidate that isn't a function template specialization, it
is not added to the list of built-in candidates. When the built-in
assignment operators are considered, the conversions from their
left-hand arguments are restricted: user-defined conversions are not
considered.
Thus, the fact that GCC/Clang ignore the existence of the user-defined overload of operator+ under the enum definition doesn't seem to be compliant with the rules listed above. The same expression inside of a block scope causes exactly same error for all compilers (and I couldn't find any rationale why this might be different for the scope of enum definition):
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
template<typename T>
T operator+(const T& lhs, const T&) {
return lhs;
}
int main() {
// error: call to non-'constexpr' function 'T operator+(const T&, const T&)
constexpr auto enumval = enum_container::item1 + enum_container::item2;
return 0;
}

GCC C++ bug? (T)x matching X::operator const T&() const differs from clang's

I've encountered a discrepancy between GCC and clang (big range of versions thereof tested at godbolt - all had same discrepancy) with conversion operator matching. Now using Barry's shorter reproduction code - also available at godbolt.
struct X {
template <typename T>
operator T const&() const;
};
int i = X();
Should operator T const&() const match when converting to int (clang says yes / GCC needs operator T() const to avoid:
<source>:6:9: error: cannot convert 'X' to 'int' in initialization
6 | int i = X();
| ^~~
The relevant part of the C++17 Standard is 15.3 Conversions [class.conv]. Therein, 15.3.5 says...
Function overload resolution (16.3.3) selects the best conversion function to perform the conversion.
...which is almost what's important. 16.3.3 is the section for Best viable function [over.match.best], but usually works in combination 16.3.2 viable functions [over.match.viable]. The Standard doesn't seem explicit about whether 16.3.2 applies here, but assuming it does, we have in 16.3.2.1:
From the set of candidate functions constructed for a given context (16.3.1), a set of viable functions is chosen
Which raises the question of whether GCC considers operator const T&() const a candidate that's not viable, or not even a candidate. The requirements for a candidate to be valid are very simple though: right number of arguments, and that "there shall exist for each argument an implicit conversion sequence (16.3.3.1) that converts that argument to the corresponding parameter...", which is obviously true here, so I believe GCC must not consider operator const T&() const a candidate, which takes us to 16.3.1 Candidate functions and argument lists [over.match.funcs], and specifically 16.3.1/7:
In each case where a candidate is a function template, candidate function template specializations are
generated using template argument deduction (17.8.3, 17.8.2). Those candidates are then handled as
candidate functions in the usual way.
Given say template <typename T> void f(const T&); could be called with an int argument type, I'd think GCC should have considered the conversion operator a valid candidate, just like clang does.
I'd appreciate confirmation / thoughts though. If folks are in agreement, I'll raise a bug report against GCC.
It is a GCC bug as pointed out by T.C..

Overload resolution, templates and inheritance

#include <iostream>
struct A {};
struct B : public A {};
template<typename T>
void foo(const T &x) { std::cout << "Called template" << std::endl; }
void foo(const A &a) { std::cout << "Called A" << std::endl; }
int main()
{
foo(A());
foo(B());
return 0;
}
This prints:
Called A
Called template
I was under the impression that a suitable non-template function would always be chosen over a template function. Can someone explain to me the resolution steps that lead to this somewhat surprising result?
I was under the impression that a suitable non-template function would always be chosen over a template function.
This only holds if the template and the non-template are equally good candidates. That's why the non-template is chosen for foo(A()).
However, in the case of foo(B()), using the non-template requires a derived-to-base conversion. So the function template is strictly better, and hence it's chosen.
The foo template instantiates into void foo(const B&). Consider what it would look like without templates:
void foo(const B &x) { std::cout << "Called template" << std::endl; }
void foo(const A &a) { std::cout << "Called A" << std::endl; }
I believe you'll agree calling foo(B()) should unambiguously pick the first one. That's exactly why the template is chosen.
n3376 13.3.3.1/6
When the parameter has a class type and the argument expression has a
derived class type, the implicit conversion sequence is a
derived-to-base Conversion from the derived class to the base class.
n3376 13.3.3.1/8
If no conversions are required to match an argument to a parameter
type, the implicit conversion sequence is the standard conversion
sequence consisting of the identity conversion (13.3.3.1.1).
Identity conversion has exact match rank due table in 13.3.3.1.1/table 12, but derived-to-base is worse, than identity.
So, compiler just have candidates in first case
// template after resolving
void foo(const A&)
// non-template
void foo(const A&)
Both has identity rank, but since first is function-template, second will be chosen.
And in second case
// template after resolving
void foo(const B&)
// non-template
void foo(const A&)
Only first has identity rank and will be chosen.
Can someone explain to me the resolution steps that lead to this somewhat surprising result?
you may look at Overload Resolution at cppreference.com:
http://en.cppreference.com/w/cpp/language/overload_resolution
in particular see the section Ranking of implicit conversion sequences
Extension of the Answer:
I tried to provide more clarification with an excerpt of the information from the aforementioned link:
A function template by itself is not a type, or a function, or any other entity. No code is generated from a source file that contains only template definitions. In order for any code to appear, a template must be instantiated: the template arguments must be determined so that the compiler can generate an actual function (or class, from a class template).
For that, the compiler goes through:
function template name lookup
template argument deduction
Down to here, the compiler has a couple of candidate function definitions which can handle the specific function call. These candidates are instannces of the template function as well as relevant non-template function definitions in the program.
But the answer to your question lies in fact here:
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before overload resolution.
The fact that function overload resolution is performed after template function instantiation is the reason for the ouput of your code.
Now your specific case goes through overload resolution as the following:
Overload Resolution:
If the [previous] steps produce more than one candidate function, then overload resolution is performed to select the function that will actually be called. In general, the candidate function whose parameters match the arguments most closely is the one that is called.
.
.
.
...
F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and
1) there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
...
.
.
.
Ranking of implicit conversion sequences:
Each type of standard conversion sequence is assigned one of three ranks:
1) Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, user-defined conversion of class type to the same class
2) Promotion: integral promotion, floating-point promotion
3) Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base
The rank of the standard conversion sequence is the worst of the ranks of the standard conversions it holds (there may be up to three conversions)
Binding of a reference parameter directly to the argument expression is either Identity or a derived-to-base Conversion:
struct Base {};
struct Derived : Base {} d;
int f(Base&); // overload #1
int f(Derived&); // overload #2
int i = f(d); // d -> Derived& has rank Exact Match
// d -> Base& has rank Conversion
// calls f(Derived&)

Unexpected ambiguity of surrogate call functions in C++

On the following code clang and EDG diagnose an ambiguous function call, while gcc and Visual Studio accept the code.
struct s
{
typedef void(*F)();
operator F(); //#1
operator F() const; //#2
};
void test(s& p)
{
p(); //ambiguous function call with clang/EDG; gcc/VS call #1
}
According to the C++ standard draft (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf) section 13.3.1.1.2 2 says;
a surrogate call function with the unique name call-function and having the form R call-function ( conversion-type-id F, P1 a1, ... ,Pn an) { return F (a1,... ,an); } is also considered as a candidate function.
In the code above that seems to mean that two call function definitions (one for each conversion function) are being considered, but both call functions have identical signatures (therefore the ambiguity) since the cv-qualifiers of the conversion operator do not seem to be taken into account in the call function signature.
I would have expected #1 to be called as with gcc and Visual Studio. So if clang/EDG are instead right in rejecting the above code, could someone please shed some light on the reason as to why the standard stipulates that there should be an ambiguity in this case and which code benefits from that property of surrogate call functions? Who is right: clang(3.5)/EDG(310) or gcc (4.8.2)/VS(2013)?
Clang and EDG are right.
Here's how this works. The standard says (same source as your quote):
In addition, for each non-explicit conversion function declared in T of the form
operator conversion-type-id () attribute-specifier-seq[opt] cv-qualifier ;
where [various conditions fulfilled in your example], a surrogate call function with the unique name call-function and having the form
R call-function ( conversion-type-id F, P1 a1, ... ,Pn an) { return F (a1, ... ,an); }
is also considered as a candidate function. [do the same for inherited conversions]^128
And the footnote points out that this may yield multiple surrogates with undistinguishable signatures, and if those aren't displaced by clearly better candidates, the call is ambiguous.
Following this scheme, your type has two conversion operators, yielding two surrogates:
// for operator F();
void call-function-1(void (*F)()) { return F(); }
// for operator F() const;
void call-function-2(void (*F)()) { return F(); }
Your example does not contain any other candidates.
The compiler then does overload resolution. Because the signatures of the two call functions are identical, it will use the same conversion sequence for both - in particular, it will use the non-const overload of the conversion function in both cases! So the two functions cannot be distinguished, and the call is ambiguous.
The key to understanding this is that the conversion that is actually used in passing the object to the surrogate doesn't have to use the conversion function the surrogate was generated for!
I can see two ways GCC and MSVC may arrive at the wrong answer here.
Option 1 is that they see the two surrogates with identical signatures, and somehow meld them into one.
Option 2, more likely, is that they thought, "hey, we don't need to do the expensive searching for a conversion for the object here, we already know that it will use the conversion function that the surrogate was generated for". It seems like a sounds optimization, except in this edge case, where this assumption is wrong. Anyway, by tying the conversion to the source conversion function, one of the surrogates uses identity-user-identity as the conversion sequence for the object, while the other uses const-user-identity, making it worse.

What are the rules for choosing from overloaded template functions?

Given the code below, why is the foo(T*) function selected ?
If I remove it (the foo(T*)) the code still compiles and works correctly, but G++ v4.4.0 (and probably other compilers as well) will generate two foo() functions: one for char[4] and one for char[7].
#include <iostream>
using namespace std;
template< typename T >
void foo( const T& )
{
cout << "foo(const T&)" << endl;
}
template< typename T >
void foo( T* )
{
cout << "foo(T*)" << endl;
}
int main()
{
foo( "bar" );
foo( "foobar" );
return 0;
}
Formally, when comparing conversion sequences, lvalue transformations are ignored. Conversions are grouped into several categories, like qualification adjustment (T* -> T const*), lvalue transformation (int[N] -> int*, void() -> void(*)()), and others.
The only difference between your two candidates is an lvalue transformation. String literals are arrays that convert to pointers. The first candidate accepts the array by reference, and thus won't need an lvalue transformation. The second candidate requires an lvalue transformation.
So, if there are two candidates that both function template specializations are equally viable by looking only at the conversions, then the rule is that the more specialized one is chosen by doing partial ordering of the two.
Let's compare the two by looking at their signature of their function parameter list
void(T const&);
void(T*);
If we choose some unique type Q for the first parameter list and try to match against the second parameter list, we are matching Q against T*. This will fail, since Q is not a pointer. Thus, the second is at least as specialized as the first.
If we do the other way around, we match Q* against T const&. The reference is dropped and toplevel qualifiers are ignored, and the remaining T becomes Q*. This is an exact match for the purpose of partial ordering, and thus deduction of the transformed parameter list of the second against the first candidate succeeds. Since the other direction (against the second) didn't succeed, the second candidate is more specialized than the first - and in consequence, overload resolution will prefer the second, if there would otherwise be an ambiguity.
At 13.3.3.2/3:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...]
S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form
defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that [...]
Then 13.3.3/1
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...]
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.5.2, or, if not that, [...]
Finally, here is the table of implicit conversions that may participate in an standard conversion sequence at 13.3.3.1.1/3.
Conversion sequences http://img259.imageshack.us/img259/851/convs.png
The full answer is quite technical.
First, string literals have char const[N] type.
Then there is an implicit conversion from char const[N] to char const*.
So both your template function match, one using reference binding, one using the implicit conversion. When they are alone, both your template functions are able to handle the calls, but when they are both present, we have to explain why the second foo (instantiated with T=char const[N]) is a better match than the first (instantiated with T=char). If you look at the overloading rules (as given by litb), the choice between
void foo(char const (&x)[4));
and
void foo(char const* x);
is ambigous (the rules are quite complicated but you can check by writing non template functions with such signatures and see that the compiler complains). In that case, the choice is made to the second one because that one is more specialized (again the rules for this partial ordering are complicated, but in this case it is because you can pass a char const[N] to a char const* but not a char const* to a char const[N] in the same way as void bar(char const*) is more specialized than void bar(char*) because you can pass a char* to a char const* but not vise-versa).
Based on overload resolution rules (Appendix B of C++ Templates: The Complete Guide has a good overview), string literals (const char []) are closer to T* than T&, because the compiler makes no distinction between char[] and char*, so T* is the closest match (const T* would be an exact match).
In fact, if you could add:
template<typename T>
void foo(const T[] a)
(which you can't), your compiler would tell you that this function is a redefinition of:
template<typename T>
void foo(const T* a)
Cause " " is a char*, which fits perfectly to foo(T*) function. When you remove this, the compiler will try to make it work with foo(T&), which requires you to pass reference to char array that contains the string.
Compiler can't generate one function that would receive reference to char, as you are passing whole array, so it has to dereference it.