A question about templates and best viable functions

Hi,

I was experimenting some code and I incurred in an unexpected error.
I reduce the code to the minimal case:

template
struct MyStruct {
operator float* () { return 0; }
operator T* () { return 0; }
};
void func(bool) {}

void func(int*) {}
int main() {
MyStruct x;
func(x);
}

Compiling the previous code result in an error with both GCC and Clang (latest version from code and 3.5 in Archlinux)

test.cpp:51:2: error: call to ‘func’ is ambiguous
func(x);
^~~~
test.cpp:45:6: note: candidate function
void func(bool) {}
^
test.cpp:46:6: note: candidate function
void func(int*) {}
^
1 error generated.

The same error can be triggered with a template operator T* () and using function template argument deduction.

MSC, instead, select func(int*) in all versions from VS2010 to VS2013 CTP 3.

Now, I was wondering which one is the expected behaviour according to the standard.
From 13.3.3 (Best Viable Function):

— 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

In this case both the function call require user defined conversion.

The one using non-template parameter yield to a standard conversion sequence of type boolean conversion (4.12).

The UDC with a template parameter should resolve in a user defined conversion followed by a standard conversion of identity type (exact match).

Intuitively I would expect this second conversion to be considered a better match than the latter one and be selected.

Far from being a language expert, I wonder if I’m misreading the standard (or missing something) or if indeed this a bug in clang.

Thanks in advance for the answer,
Samuele

Hi,

I was experimenting some code and I incurred in an unexpected error.
I reduce the code to the minimal case:

template <class T>
struct MyStruct {
  operator float* () { return 0; }
  operator T* () { return 0; }
};
void func(bool) {}
void func(int*) {}
int main() {
  MyStruct<int> x;
  func(x);
}

This isn’t minimal. There’s no need for the template; this code yields the same results (in Clang, at least):

struct MyStruct {
  operator float* () { return 0; }
  operator int* () { return 0; }
};
void func(bool) {}
void func(int*) {}
int main() {
  MyStruct x;
  func(x);
}

Now, I was wondering which one is the expected behaviour according to the standard.
From 13.3.3 (Best Viable Function):

— 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

This bullet point doesn’t apply. The context that is ambiguous is the function call func(x), not the decision between the two conversion operators (that’s a separate, nested overloading decision while determining the conversion sequences for each of the two variants of func).

In this case both the function call require user defined conversion.

The one using non-template parameter yield to a standard conversion sequence of type boolean conversion (4.12).

The UDC with a template parameter should resolve in a user defined conversion followed by a standard conversion of identity type (exact match).

Intuitively I would expect this second conversion to be considered a better match than the latter one and be selected.

I agree that it is intuitive, but let’s look at it more formally.

When the compiler sees func(x), it has to decide between calling func(bool) and func(int*). The argument in either case is of type MyStruct. To decide, it individually finds the conversion sequences for all parameters of all candidates.

func(bool) can be called in two ways: MyStruct->int*->bool and MyStruct->float*->bool. Since neither is better than the other, the conversion is the “ambiguous conversion sequence” (13.3.3.1p10), which "is treated as a user-defined sequence that is indistinguishable from any other user-defined conversion sequence”.

func(int*) can be called in one way: MyStruct->int*. This is a user-defined conversion sequence.

Since the ACS for func(bool) is, by definition, indistinguishable from the UDCS for func(int*), the call is ambiguous. Clang and GCC are correct, MSVC is wrong.

This case is an unfortunate casualty of preventing a more dangerous unintuitive situation, as described in the footnote referenced by my quoted standard paragraph. Making this particular case work again would require that the ambiguous conversion sequence somehow keep its trailing standard conversion sequences around (or at least the best one) for the purpose of comparing it to the trailing standard conversion sequence in other overloads - a significant increase in complexity for overload resolution.

Sebastian

That makes more sense now.
Thank you very much for the answer

> Hi,
>
> I was experimenting some code and I incurred in an unexpected error.
> I reduce the code to the minimal case:
>
> template <class T>
> struct MyStruct {
> operator float* () { return 0; }
> operator T* () { return 0; }
> };
> void func(bool) {}
> void func(int*) {}
> int main() {
> MyStruct<int> x;
> func(x);
> }

This isn’t minimal. There’s no need for the template; this code yields the
same results (in Clang, at least):

struct MyStruct {
        operator float* () { return 0; }
        operator int* () { return 0; }
};
void func(bool) {}
void func(int*) {}
int main() {
        MyStruct x;
        func(x);
}

This gives a rather different problem. In the original question, we had

MyStruct -> float* -> bool -> void func(bool)
MyStruct -> T* (T = int) -> void func(int*)

(Note that we can't use the template for the bool overload of func, because
we cannot deduce T.) In your version, we have an ambiguous conversion
sequence for MyStruct and a user-defined conversion sequence for func(int*).

In the original version, the reason for ambiguity is that user-defined
conversion sequences with different conversion functions are incomparable.
(See 13.3.3.2/3.2.)

The struct is the template, not the conversion operator. And once the struct template is instantiated, there is no difference to a non-template struct.

Sebastian

Sorry, right you are =)