User account creation filtered due to spam.

Bug 49058 - [C++0x] Bind no-arguments functor failed using std::bind with -pedantic option.
Summary: [C++0x] Bind no-arguments functor failed using std::bind with -pedantic option.
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.5.4
: P3 normal
Target Milestone: 4.6.1
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-05-19 07:07 UTC by Kohei Takahashi
Modified: 2011-05-24 23:37 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-05-19 15:43:41


Attachments
Fix (734 bytes, patch)
2011-05-19 16:09 UTC, Jason Merrill
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Kohei Takahashi 2011-05-19 07:07:58 UTC
GCC rejects following code with -pedantic option.

==== testcase.cc ====
#include <functional>

struct F
{
	void
	operator()();
};

void f()
{
	std::bind( F() );
}
====

==== output ====
$ g++-4.5 -std=c++0x -pedantic testcase.cc
In file included from testcase.cc:1:0:
/usr/local/lib/gcc/i686-pc-linux-gnu/4.5.4/../../../../include/c++/4.5.4/functional: In instantiation of 'std::_Bind<F()>':
testcase.cc:11:20:   instantiated from here
/usr/local/lib/gcc/i686-pc-linux-gnu/4.5.4/../../../../include/c++/4.5.4/functional:1174:39: error: no match for call to '(const F) ()'
testcase.cc:5:5: note: candidate is: void F::operator()() <near match>
====

After GCC 4.6, also faild with no-arguments lambda-expression.

==== testcase.cc ====
#include <functional>

void f()
{
    std::bind( []{} );
}
====

==== output ====
$ g++-4.6 -std=c++0x -pedantic testcase.cc
In file included from testcase.cc:1:0:
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional: In instantiation of 'std::_Bind<f()::<lambda()>()>':
testcase.cc:5:21:   instantiated from here
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1227:32: error: no match for call to '(volatile f()::<lambda()>) ()'
testcase.cc:5:17: note: candidates are:
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1227:32: note: void (*)() <conversion>
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1227:32: note:   candidate expects 0 arguments, 1 provided
testcase.cc:5:17: note: f()::<lambda()> <near match>
testcase.cc:5:17: note:   no known conversion for implicit 'this' parameter from 'volatile f()::<lambda()>*' to 'const f()::<lambda()>*'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional: In instantiation of 'std::_Bind<f()::<lambda()>()>':
testcase.cc:5:21:   instantiated from here
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1240:38: error: no match for call to '(const volatile f()::<lambda()>) ()'
testcase.cc:5:17: note: candidates are:
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1240:38: note: void (*)() <conversion>
/usr/local/lib/gcc/i686-pc-linux-gnu/4.6.1/../../../../include/c++/4.6.1/functional:1240:38: note:   candidate expects 0 arguments, 1 provided
testcase.cc:5:17: note: f()::<lambda()> <near match>
testcase.cc:5:17: note:   no known conversion for implicit 'this' parameter from 'const volatile f()::<lambda()>*' to 'const f()::<lambda()>*'
====

gcc-4.5 (GCC) 4.5.4 20110512 (prerelease) - Failed
gcc-4.6 (GCC) 4.6.1 20110513 (prerelease) - Failed
gcc-4.7 (GCC) 4.7.0 20110517 (experimental) - Failed
Comment 1 Jonathan Wakely 2011-05-19 10:47:15 UTC
Jason, this works without -pedantic but fails with:

template<typename T> T val();

struct F1
{
    void operator()();
};

template<typename... T> struct tuple { };

template<typename T, typename... V>
T
call(T&& t, tuple<V...>)
{ return t; }

template<typename F, typename... T>
struct Bind
{
    template<typename... A, typename R
      = decltype( val<F>()( call(val<T>(), tuple<A...>())... ) )>
    R f(A&&...);

    template<typename... A, typename R
      = decltype( val<const F>()( call(val<T>(), tuple<A...>())... ) )>
    R f(A&&...) const;
};

int main()
{
    Bind<F1>().f();
}

Is this code relying on a G++ extension?

The parameter pack T has zero elements so the expansion:
    call(val<T>(), tuple<A...>())... )
expands to nothing, and in -pedantic mode that seems to prevent the parameter pack A from participating in type deduction, so SFINAE doesn't remove the overload.
Comment 2 Jason Merrill 2011-05-19 15:43:41 UTC
This has nothing to do with variadics; the same error occurs with

template<typename T> T val();

struct F1
{   
    void operator()();
};

template<typename F>
struct Bind
{   
    template<typename R
      = decltype( val<F>()( ) )>
    R f();

    template<typename R
      = decltype( val<const F>()( ) )>
    R f() const;
};

int main()
{   
    Bind<F1>().f();
}

The problem is that F1::operator() is non-const, so when we instantiate Bind<F1>, val<const F>() is ill-formed.  And this error is not dependent on   EDG also rejects this testcase.  The bug is that we silently accept it without -pedantic; we should be giving a pedwarn.
Comment 3 Jason Merrill 2011-05-19 16:09:05 UTC
Created attachment 24291 [details]
Fix

Here's a fix, which breaks bind, so I'm not going to check it in yet.  Is there already a libstdc++ bug for this issue that I can mark as blocking this one?
Comment 4 Jason Merrill 2011-05-19 16:09:59 UTC
Actually I'll just unassign myself and change the category.
Comment 5 Jason Merrill 2011-05-19 16:10:58 UTC
...and adding Jonathan to CC.  See my earlier comments: a compiler bug was masking a library bug.
Comment 6 Jonathan Wakely 2011-05-19 16:52:35 UTC
thanks, I'll deal with the lib bug
Comment 7 Paolo Carlini 2011-05-22 20:44:49 UTC
Guys, do we have solid reasons to believe that the below totally stupid tweak is not Ok? Because it passes the testsuite and the new testcases here with -pedantic:

Index: include/std/functional
===================================================================
--- include/std/functional	(revision 174039)
+++ include/std/functional	(working copy)
@@ -1210,7 +1210,7 @@
 
       // Call as const
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const _Functor>()(
+	= decltype( std::declval<_Functor>()(
 	      _Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1223,7 +1223,7 @@
 
       // Call as volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<volatile _Functor>()(
+	= decltype( std::declval<_Functor>()(
 	      _Mu<_Bound_args>()( std::declval<volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1236,7 +1236,7 @@
 
       // Call as const volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const volatile _Functor>()(
+	= decltype( std::declval<_Functor>()(
 	      _Mu<_Bound_args>()( std::declval<const volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
Comment 8 Jonathan Wakely 2011-05-22 21:23:12 UTC
It would be wrong though, consider:

struct F {
  int operator();
  void operator() const;
};

the decltype would detect the return type as 'int' but the function body would return void, because _M_f is const
Comment 9 Jonathan Wakely 2011-05-22 21:24:16 UTC
Duh, sorry, that should have been

struct F {
  int operator()();
  void operator()() const;
};
Comment 10 Jonathan Wakely 2011-05-22 21:29:56 UTC
For a complete example:

struct F {
  int operator()() const { return 0; }
  void operator()() const { };
};

int main()
{
  auto const b = std::bind( F() );
  int i = b();  // b is const here, must return int
}

[func.bind.bind] p3 requires that the cv-qualifiers of the call wrapper are used when calling the target object
Comment 11 Jonathan Wakely 2011-05-22 21:32:05 UTC
(In reply to comment #10)
> For a complete example:
> 
> struct F {
>   int operator()() const { return 0; }
>   void operator()() const { };

sigh, I should really take a bit longer before hitting send!

struct F {
  int operator()() const { return 0; }
  void operator()() { };
};
Comment 12 Paolo Carlini 2011-05-22 21:47:04 UTC
Ok, let's make sure to have something similar in the testsuite.

And, well, I still believe a tangle of auto return types shouldn't be really necessary in order to solve the problem, to be honest ;)
Comment 13 Paolo Carlini 2011-05-22 22:17:10 UTC
What about creating an artificially dependent context, thus amenable to SFINAE, like:

// Call as const
template<typename... _Args, typename _Result
  = decltype( std::declval<typename conditional<(sizeof...(_Args) >= 0),
    typename add_const<_Functor>::type,
typename add_const<_Functor>::type>::type>()

(
              _Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
                                  std::declval<tuple<_Args...>&>() )... ) )>
        _Result
Comment 14 Paolo Carlini 2011-05-22 22:20:24 UTC
Sorry, hit return inadvertently. Anyway, the idea would be, instead of just writing:

  const _Functor

writing something like:

  typename conditional<(sizeof...(_Args) >= 0,
                       typename add_const<_Functor>::type,
                       typename add_const<_Functor>::type>::type

I think that should work, it does in some simple tests I did, and all in all, properly polished, doesn't even look too bad, IMHO.
Comment 15 Paolo Carlini 2011-05-22 22:30:55 UTC
This is something which actually passes the testsuite, and the tests here and the new test explained by Jon. Maybe something even more clean along the same lines is possible, but I would be already pretty happy for now... assuming Jon isn't able to figure out yet another regression ;)

////////////////////

Index: include/std/functional
===================================================================
--- include/std/functional	(revision 174044)
+++ include/std/functional	(working copy)
@@ -1210,7 +1210,8 @@
 
       // Call as const
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const _Functor>()(
+	= decltype( std::declval<typename conditional<(sizeof...(_Args) >= 0),
+		       typename add_const<_Functor>::type, void>::type>()(
 	      _Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1223,7 +1224,8 @@
 
       // Call as volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<volatile _Functor>()(
+	= decltype( std::declval<typename conditional<(sizeof...(_Args) >= 0),
+                       typename add_volatile<_Functor>::type, void>::type>()(
 	      _Mu<_Bound_args>()( std::declval<volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1236,7 +1238,8 @@
 
       // Call as const volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const volatile _Functor>()(
+	= decltype( std::declval<typename conditional<(sizeof...(_Args) >= 0),
+                       typename add_cv<_Functor>::type, void>::type>()(
 	      _Mu<_Bound_args>()( std::declval<const volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
Comment 16 Paolo Carlini 2011-05-22 22:52:13 UTC
With enable_if becomes a tad more concise.

///////////////////

Index: include/std/functional
===================================================================
--- include/std/functional	(revision 174044)
+++ include/std/functional	(working copy)
@@ -1210,7 +1210,8 @@
 
       // Call as const
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const _Functor>()(
+	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
+		       typename add_const<_Functor>::type>::type>()(
 	      _Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1223,7 +1224,8 @@
 
       // Call as volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<volatile _Functor>()(
+	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
+                       typename add_volatile<_Functor>::type>::type>()(
 	      _Mu<_Bound_args>()( std::declval<volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
@@ -1236,7 +1238,8 @@
 
       // Call as const volatile
       template<typename... _Args, typename _Result
-	= decltype( std::declval<const volatile _Functor>()(
+	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
+                       typename add_cv<_Functor>::type>::type>()(
 	      _Mu<_Bound_args>()( std::declval<const volatile _Bound_args&>(),
 				  std::declval<tuple<_Args...>&>() )... ) )>
 	_Result
Comment 17 Jonathan Wakely 2011-05-22 23:09:06 UTC
nice trick, if that passes the testsuite I'd check it in
Comment 18 paolo@gcc.gnu.org 2011-05-23 00:05:29 UTC
Author: paolo
Date: Mon May 23 00:05:24 2011
New Revision: 174048

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=174048
Log:
2011-05-22  Jonathan Wakely  <jwakely.gcc@gmail.com>

	* testsuite/20_util/bind/cv_quals_2.cc: New.

2011-05-22  Paolo Carlini  <paolo.carlini@oracle.com>

	PR libstdc++/49058
	* include/std/functional (_Bind<_Functor(_Bound_args...)>::
	operator()(_Args&&...)): Don't cv qualify _Functor directly
	in the default template argument, SFINAE doesn't apply when
	the functor has no arguments.
	* testsuite/20_util/bind/49058_1.cc: New.
	* testsuite/20_util/bind/49058_2.cc: Likewise.

Added:
    trunk/libstdc++-v3/testsuite/20_util/bind/49058_1.cc
    trunk/libstdc++-v3/testsuite/20_util/bind/49058_2.cc
    trunk/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/include/std/functional
Comment 19 paolo@gcc.gnu.org 2011-05-23 00:09:00 UTC
Author: paolo
Date: Mon May 23 00:08:52 2011
New Revision: 174049

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=174049
Log:
2011-05-22  Jonathan Wakely  <jwakely.gcc@gmail.com>

	* testsuite/20_util/bind/cv_quals_2.cc: New.

2011-05-22  Paolo Carlini  <paolo.carlini@oracle.com>

	PR libstdc++/49058
	* include/std/functional (_Bind<_Functor(_Bound_args...)>::
	operator()(_Args&&...)): Don't cv qualify _Functor directly
	in the default template argument, SFINAE doesn't apply when
	the functor has no arguments.
	* testsuite/20_util/bind/49058_1.cc: New.
	* testsuite/20_util/bind/49058_2.cc: Likewise.

Added:
    branches/gcc-4_6-branch/libstdc++-v3/testsuite/20_util/bind/49058_1.cc
    branches/gcc-4_6-branch/libstdc++-v3/testsuite/20_util/bind/49058_2.cc
    branches/gcc-4_6-branch/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
Modified:
    branches/gcc-4_6-branch/libstdc++-v3/ChangeLog
    branches/gcc-4_6-branch/libstdc++-v3/include/std/functional
Comment 20 Paolo Carlini 2011-05-23 00:10:24 UTC
Thanks Jon. Thus, library issue fixed in mainline and 4_6-branch.

Jason, I think you can safely commit the compiler change in mainline...
Comment 21 Jason Merrill 2011-05-23 15:32:42 UTC
Author: jason
Date: Mon May 23 15:32:39 2011
New Revision: 174073

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=174073
Log:
	PR c++/49058
	* call.c (splice_viable): Be strict in templates.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/sfinae24.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/testsuite/ChangeLog
Comment 22 Paolo Carlini 2011-05-24 23:37:29 UTC
Unless Jason wants to apply the C++ patch to the release branch too, this is all done.