Bug 80564 - bind on SFINAE unfriendly generic lambda
Summary: bind on SFINAE unfriendly generic lambda
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: c++-lambda, rejects-valid
Depends on:
Blocks:
 
Reported: 2017-04-29 12:53 UTC by Robert Haberlach
Modified: 2024-04-15 23:09 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Robert Haberlach 2017-04-29 12:53:16 UTC
Related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49058

---
#include <functional>

int main() {
    int i;
    std::bind([] (auto& x) {x = 1;}, i)();
}

---

This is rejected because, during overload resolution, _Bind::operator() const's default template argument is spuriously instantiated.
Comment 1 Eric Fiselier 2017-05-03 22:16:38 UTC
Note that the instantiation is not spurious, but instead required by the core language. All overloads of _Bind::operator() are considered during the call to the forwarding call wrapper.

While considering the const qualified overload the compiler is forced to instantiate the lambda to deduce the return type. This causes a error in a non-immediate context which causes the compile error.
Comment 2 Eric Fiselier 2017-05-03 22:17:51 UTC
Note that explicitly providing a return type for the lambda avoids this compile error. Example:

---
#include <functional>

int main() {
    int i;
    std::bind([] (auto& x) -> void {x = 1;}, i)(); // OK!
}

---
Comment 3 Eric Fiselier 2017-05-03 22:39:43 UTC
Here is an example of why `_Bind::operator()(...) const` must be considered during overload resolution even if the call wrapper itself is not const.

------------------------------
#include <functional>

struct Func {
  template <class ...Args>
  void operator()(Args&&...) = delete;

  template <class ...Args>
  void operator()(Args&&...) const {}
};

int main() {
    Func f;
    std::bind(f)();
}
-------------------------
Comment 4 TC 2017-05-04 20:09:14 UTC
(In reply to Eric Fiselier from comment #3)
> Here is an example of why `_Bind::operator()(...) const` must be considered
> during overload resolution even if the call wrapper itself is not const.
> 
> ------------------------------
> #include <functional>
> 
> struct Func {
>   template <class ...Args>
>   void operator()(Args&&...) = delete;
> 
>   template <class ...Args>
>   void operator()(Args&&...) const {}
> };
> 
> int main() {
>     Func f;
>     std::bind(f)();
> }
> -------------------------

Interesting, libstdc++ rejects this as an attempt to call a deleted function. That seems more correct than libc++'s approach which calls the const overload.
Comment 5 r.hl 2017-05-04 20:14:05 UTC
See also the discussion on Phabricator: https://reviews.llvm.org/D32824

I agree; AFAICS [func.bind.bind] is clear on this: the type of the Func
object used to call the member operator() is non-const.

On 5/4/2017 9:09 PM, rs2740 at gmail dot com wrote:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80564
>
> TC <rs2740 at gmail dot com> changed:
>
>            What    |Removed                     |Added
> ----------------------------------------------------------------------------
>                  CC|                            |rs2740 at gmail dot com
>
> --- Comment #4 from TC <rs2740 at gmail dot com> ---
> (In reply to Eric Fiselier from comment #3)
>> Here is an example of why `_Bind::operator()(...) const` must be considered
>> during overload resolution even if the call wrapper itself is not const.
>>
>> ------------------------------
>> #include <functional>
>>
>> struct Func {
>>   template <class ...Args>
>>   void operator()(Args&&...) = delete;
>>
>>   template <class ...Args>
>>   void operator()(Args&&...) const {}
>> };
>>
>> int main() {
>>     Func f;
>>     std::bind(f)();
>> }
>> -------------------------
> Interesting, libstdc++ rejects this as an attempt to call a deleted function.
> That seems more correct than libc++'s approach which calls the const overload.
>
Comment 6 Robert Haberlach 2017-05-04 20:14:52 UTC
(In reply to TC from comment #4)
> (In reply to Eric Fiselier from comment #3)
> > Here is an example of why `_Bind::operator()(...) const` must be considered
> > during overload resolution even if the call wrapper itself is not const.
> > 
> > ------------------------------
> > #include <functional>
> > 
> > struct Func {
> >   template <class ...Args>
> >   void operator()(Args&&...) = delete;
> > 
> >   template <class ...Args>
> >   void operator()(Args&&...) const {}
> > };
> > 
> > int main() {
> >     Func f;
> >     std::bind(f)();
> > }
> > -------------------------
> 
> Interesting, libstdc++ rejects this as an attempt to call a deleted
> function. That seems more correct than libc++'s approach which calls the
> const overload.

See also the discussion on Phabricator: https://reviews.llvm.org/D32824

I agree. AFAICS [func.bind.bind] is clear on this: the type of the Func
object used to call the member operator() is non-const.
Comment 7 Robert Haberlach 2017-05-04 20:15:55 UTC
Oh, damn. "Submit only my new comment" does not what I thought it does. :-)