Why does this code fragment compile with VS2010 but not GCC 4.5.2?
#include <functional>
#include <memory>
#include <iostream>
using namespace std;
class Foo
{
public:
void Bar() { std::cout << "Foo::Bar" << std::endl; }
};
int main()
{
shared_ptr<Foo> foo(new Foo);
function<void(Foo*)> f1(bind(&Foo::Bar, placeholders::_1));
function<void(shared_ptr<Foo>)> f2(bind(&Foo::Bar, placeholders::_1));
return 0;
}
GCC objects to the second bind statement being assigned to the function object with the shared_ptr signature. Here is the error output.
开发者_JAVA百科/usr/include/c++/4.5/functional:2103|6|instantiated from ‘std::function<_Res(_ArgTypes ...)>::function(_Functor, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type) [with _Functor = std::_Bind(std::_Placeholder<1>)>, _Res = void, _ArgTypes = {std::shared_ptr}, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type = std::function)>::_Useless]’| /home/craig/work/litd/test/main.cpp:29|97|instantiated from here| /usr/include/c++/4.5/functional|1713|error: no match for call to ‘(std::_Bind(std::_Placeholder<1>)>) (std::shared_ptr)’| ||=== Build finished: 1 errors, 0 warnings ===|
Edit: More mystery, when I change the include headers to their tr1 equivalents, it does compile.
#include <tr1/functional>
#include <tr1/memory>
#include <iostream>
using namespace std::tr1;
class Foo
{
public:
void Bar() { std::cout << "Foo::Bar" << std::endl; }
};
int main()
{
shared_ptr<Foo> foo(new Foo);
function<void(Foo*)> f1(bind(&Foo::Bar, placeholders::_1));
function<void(shared_ptr<Foo>)> f2(bind(&Foo::Bar, placeholders::_1));
return 0;
}
It looks like a bug in g++'s implementation of std::function
or maybe std::bind
, depending on whether you can invoke the object returned by bind(&Foo::Bar, placeholders::_1)
with foo
; if this works:
auto fn2 = bind(&Foo::Bar, placeholders::_1);
fn2(foo);
then it would seem that g++'s std::function
implementation is incomplete. Otherwise, it would seem that the implementation of std::bind
is incomplete.
[20.8.9.1.2] Function template bind
states:
template<class F, class... BoundArgs> unspecified bind(F&& f, BoundArgs&&... bound_args);
...
Returns: A forwarding call wrapper
g
with a weak result type (20.8.2). The effect ofg(u1, u2, ..., uM)
shall beINVOKE(fd, v1, v2, ..., vN, result_of::type)
[20.8.2] Requirements states:
Define
INVOKE(f, t1, t2, ..., tN)
as follows:—
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;—
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;...
When binding &Foo::Bar
, the returned "forwarding call wrapper" takes one argument, u1
. Call its type U1
. Further on in [20.8.9.1.2] it states that because the 1st template argument type in BoundArgs
was the _1
placeholder type, the type V1
is U1&&
.
Passing a std::shared_ptr<Foo>
to the forwarding call wrapper returned by bind(&Foo::Bar, placeholders::_1)
should be allowed because case 2 of [20.8.2] applies.
EDIT: I am using the same version of g++ as you, 4.5.2, on Windows (MinGW). For me, the following compiles just fine:
#include <functional>
#include <memory>
#include <iostream>
using namespace std;
class Foo
{
public:
void Bar() { std::cout << "Foo::Bar" << std::endl; }
};
int main()
{
shared_ptr<Foo> foo(new Foo);
function<void(Foo*)> f1(bind(&Foo::Bar, placeholders::_1));
//function<void(shared_ptr<Foo>)> f2(bind(&Foo::Bar, placeholders::_1));
auto fn2 = bind(&Foo::Bar, placeholders::_1);
fn2(foo);
return 0;
}
It thus appears to be g++'s implementation of std::function
that is to blame.
EDIT2: The following fails:
auto fn2 = bind(&Foo::Bar, placeholders::_1);
fn2(std::shared_ptr<Foo>(foo));
SO7408263.cpp:19:31: error: no match for call to '(std::_Bind(std::_Placeholder<1>)>) (std::shared_ptr)'
Perhaps it's std::bind
after all.
The function will expect ->*
to be overloaded to use a pointer-to-member. I believe that shared_ptr
does not provide this functionality. The TR1 specification might have mandated a specialization but the C++11 might not. In Visual Studio then the C++11 shared_ptr
is defined to be their TR1 versions, which would explain the difference.
精彩评论