[patch] [C++14] Implement N3657: heterogeneous lookup in associative containers.
Jonathan Wakely
jwakely@redhat.com
Sat Jan 24 23:00:00 GMT 2015
[Dropping gcc-patches@ from the CC list.]
On 24/01/15 22:46 +0000, Jonathan Wakely wrote:
>On 24/01/15 23:03 +0100, François Dumont wrote:
>>@@ -1155,9 +1150,8 @@
>> return _S_iter(__y);
>> }
>>
>>- template<typename _Kt,
>>- typename _Req = typename __is_transparent<_Compare, _Kt>::type>
>>- iterator
>>+ template<typename _Kt>
>>+ enable_if_t<__has_is_transparent<_Compare>::value, iterator>
>> _M_find_tr(const _Kt& __k)
>
>This doesn't work.
>
>Consider:
>
> #include <set>
>
> struct I
> {
> int i;
> operator int() const { return i; }
> };
>
> int main()
> {
> std::set<int> s;
> I i = { };
> s.find(i);
> }
>
>(I will add something like this to the testsuite.)
>
>This program is valid according to any C++ standard, but fails to
>compile with your patch applied because overload resolution
>instantiates std::_Rb_tree<>::_M_find_tr<I> which instantiates
>enable_if<false, iterator>::type, which is an error. SFINAE does not
>apply, because the invalid type enable_if<false, iterator>::type is
>not found during *substitution*. It's just invalid, so when _Compare
>is not transparent, instantiating the function template is simply an
>error.
Here's a simpler example explaining the problem:
#include <type_traits>
template<typename T>
struct trait : std::false_type
{ };
template<typename T>
struct X {
template<typename U>
typename std::enable_if<trait<T>::value, int>::type
f() { return 1; }
int f() const { return 0; }
};
int main()
{
const X<int> x{};
return x.f();
}
Obviously this should call the non-template X<int>::f(), but overload
resolution tries to instantiate the template X<int>::f<U>() but that
uses the invalid type enable_if<false, int>::type. At the point where
the invalid type is discovered we haven't even deduced U yet, let
alone attempted substituting the deduced type into the template, so
this is not a substition failure.
>Observe that my __is_transparent alias template takes two template
>arguments, so that it depends on the template parameter of the
>function, not only on _Compare. That means whether if the type is
>invalid that will be found during template argument substitution, so
>SFINAE applies.
The equivalent fix to the example above would be:
template<typename T, typename U> // !!! second parameter
struct trait : std::false_type
{ };
template<typename T>
struct X {
template<typename U>
typename std::enable_if<trait<T, U>::value, int>::type
f() { return 1; }
int f() const { return 0; }
};
Now the compiler doesn't know whether enable_if<>::type is valid or
not until after deducing and substituting U (which never happens
at all in the example because U can't be deduced in the x.f() call).
More information about the Libstdc++
mailing list