Casey Carter recently posted this to the committee core- mailing list: <<<<<<<<<<< BEGIN It is a commonplace library technique to compose function objects, which are often empty, by inheritance to take advantage of the Empty Base Optimization. E.g., this program patterned on usage in range-v3 turns a function that accepts objects into a function that accepts iterators that denote objects: template <class T> T&& declval() noexcept; template <class, class> constexpr bool is_same = false; template <class T> constexpr bool is_same<T, T> = true; template <class F> struct indirected : F { indirected(F f) : F(f) {} template <class I> auto operator()(I i) -> decltype(declval<F&>()(*i)) { return static_cast<F&>(*this)(*i); } }; int main() { auto f = [](auto rng) { static_assert(is_same<decltype(rng), int>, ""); return 42; }; indirected<decltype(f)> i(f); static_assert(is_same<decltype(i(declval<int*>())), int>, ""); } Unfortunately, when the adapted function object is an underconstrained captureless generic lambda - as is the case in the example - composition by inheritance is extremely fragile. Since the lambda is captureless, its closure type has a member conversion operator that converts to a function pointer type with deduced return type. An attempt to call the derived type's call operator results in overload resolution instantiating the conversion operator's declaration necessitating return type deduction from the base object's call operator. When the base object's call operator is ill-formed for the particular argument types - again as is the case in the example - the program is ill-formed. GCC 6 and trunk tell me this program is ill-formed: casey@Semiregular:~/bugs$ ~/gcc/bin/g++ -std=c++14 repro.cpp repro.cpp: In instantiation of ‘main()::<lambda(auto:1)> [with auto:1 = int*]’: repro.cpp:17:25: required by substitution of ‘template<class auto:1> main()::<lambda(auto:1)>::operator decltype (((main()::<lambda(auto:1)>)0u).operator()(static_cast<auto:1>(<anonymous>))) (*)(auto:1)() const [with auto:1 = int*]’ repro.cpp:22:53: required from here repro.cpp:18:9: error: static assertion failed static_assert(is_same<decltype(rng), int>, ""); ^~~~~~~~~~~~~ older versions of GCC and all versions of Clang I've tried compile the program without diagnostics. Since hiding of conversion operators is based on the target types, and determining the target type of the problematic conversion operator results in the aforementioned ill-formed return type deduction, there seems to be no way to hide the problematic conversion operator. >>>>>>>>>>>>>>>>>>>>>>>> END To which Richard Smith replied: <<<<<<<<<<<<<<<<<<<<<<<< BEGIN template<typename T> using id = T; struct F { template<typename T> operator id<T(*)(T)>(); } f; int n = f(0); GCC accepts this and calls the conversion function template with T=int. Clang, EDG, MSVC reject. Per [over.call.object]/2, I think GCC is wrong per the current language wording. A conversion function template does not qualify as a "non-explicit conversion function declared in [F or base class thereof]" (because it is not a conversion function), and nor does a specialization of one (because it is not itself declared in F). I also don't see any way the current wording would take us to [temp.deduct.conv] for this case, nor how that wording would apply (since we don't have a function type that's required as the result of the conversion). Perhaps GCC is following the rules of [temp.deduct.call] in this case, treating the (pointee type of the) result type of the conversion function as if it were the function type of the callee? In the abstract, GCC's approach to this situation seems superior -- it's able to use a conversion to function pointer in many cases where other compilers can't -- but I'm a little hesitant to suggest we adopt that approach since it breaks examples like yours. >>>>>>>>>>>>>>>>>>>>>>>>> END
Very closely related, but not an exact duplicate of, PR 71105.
The second testcase started to be accepted with r229210 change and similarly r229209 accepted the first testcase, while r229210 rejects it.
BTW, the first testcase has been rejected at gcc-5-branch branch point, only got accepted later on in r226642 (and that change has been later backported).
Author: jason Date: Fri Jul 15 18:49:25 2016 New Revision: 238394 URL: https://gcc.gnu.org/viewcvs?rev=238394&root=gcc&view=rev Log: PR c++/71117 - core 2189 and generic lambda * call.c (add_template_conv_candidate): Disable if there are viable candidates. Added: trunk/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv2.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/call.c trunk/gcc/testsuite/g++.dg/cpp0x/conv-tmpl1.C
*** Bug 70942 has been marked as a duplicate of this bug. ***
Author: jason Date: Thu Jul 21 06:16:11 2016 New Revision: 238573 URL: https://gcc.gnu.org/viewcvs?rev=238573&root=gcc&view=rev Log: PR c++/71117 - core 2189 and generic lambda * call.c (add_template_conv_candidate): Disable if there are viable candidates. Added: branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv2.C branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv3.C Modified: branches/gcc-6-branch/gcc/cp/ChangeLog branches/gcc-6-branch/gcc/cp/call.c branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp0x/conv-tmpl1.C
Fixed.
*** Bug 71095 has been marked as a duplicate of this bug. ***