I have some code that uses template conversion operator to find return type of function found through ADL.
The simplified code look like this:
#include <type_traits>
template<typename S>
struct probe {
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T& ();
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T&&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T&& ();
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T const&, U>::value, int> = 0>
operator T const& () const;
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T const&&, U>::value, int> = 0>
operator T const&& () const;
};
namespace foo {
struct bar {};
auto find_me(bar const&) -> int { return 0; }
}
int main() {
// That would be inside a template in my code.
find_me(probe<foo::bar>{});
}
In clang 6 and GCC, the above code compiles. However, in Clang 7, it doesn't compile anymore!
As you can see, clang 6 resolve the call to probe<foo::bar>::operator foo::bar&&<foo::bar, foo::bar&&, 0>()
but clang 7 fails because it tries to call probe<foo::bar>::operator const foo::bar&&<const foo::bar, foo::bar&&, 0>()
Which compiler is right? What is the rule in the standard for this? Is this a new Clang bug or is it a fix?
There are many cases I want to check. Not just foo::bar
as parameter, but many reference types, such as this:
namespace foo {
struct bar {};
auto find_me(bar const&) -> int { return 0; }
auto find_me(bar&&) -> int { return 0; }
auto find_me(bar const&&) -> int { return 0; }
auto find_me(bar&) -> int { return 0; }
}
int main() {
find_me(probe<foo::bar>{});
find_me(probe<foo::bar&>{});
find_me(probe<foo::bar&&>{});
find_me(probe<foo::bar const&>{});
find_me(probe<foo::bar const&&>{});
}
Resolving to the right function call is important.
Here's a live example of all those cases, GCC succeed but clang fails: https://godbolt.org/z/yrDFMg
The difference in behavior between clang 6/7 and gcc is illustrated by this simplified example code:
Gcc and Clang 6 accept the code, and clang 7 rejects it.
In the case of Gcc both
T=int
andT=const int
are considered cases. For clang 7 onlyT=const int
. BecauseT=const int
is disabled, clang 7 reject the code.According to [over.match.ref]:
In our case this means that convertion of
S
toint&
orconst int&
could be candidate.And [temp.deduct.conv]:
So I think two literal readings are acceptable:
gcc considers that the result of the conversion does not mean the result of the conversion sequence, so it first decides that which conversion sequence are acceptable according to [over.match.ref] and then perform template argument deduction for the conversion operator for all the possible conversion sequences.
clang considers that the result of the conversion does mean the target of the conversion sequence. And it performs argument deduction only for
T=cont int
.From what I have read in the standard I cannot say what is the "right" interpretation of the standard. Nevertheless I think that clang behavior is more consistent with template argument deduction in general: