Mixing std::tr1::shared_ptr with std::function / std::bind causes compiler errors with newer gcc

482 Views Asked by At

I need to use some old legacy code which uses std::tr1::shared_ptr. The header that I need to include has both #include <tr1/memory> and #include <tr1/functional>. In my code I want to use std::function and std::bind, since our compiler supports these.

This works fine with gcc 4.6.3 and 4.7.3. In 4.9.2, 5.1.x and 5.2.0 this results in a compiler error. It seems to happen due to the <tr1/functional>-include.

I wrote a small example that reproduce this problem:

#include <tr1/memory>
#include <functional>
#include <tr1/functional>

struct Foo {
    void test(std::tr1::shared_ptr<int> i) {}
};

int main() {
    Foo f;
    std::function<void(std::tr1::shared_ptr<int>)> func = std::bind(&Foo::test, f, std::placeholders::_1);

    return 0;
}

gcc-4.6.3, without #include <tr1/functional>, compiles OK. Tested on my local machine.

gcc-4.6.3, with #include <tr1/functional>, compiles OK. Tested on my local machine.

gcc-5.1, without #include <tr1/functional>, compiles OK: http://ideone.com/XrkAXT

gcc-5.1, with #include <tr1/functional>, compiler error: http://ideone.com/sRWQLn

I'm not using any std::tr1::function or std::tr1::bind at all, and the only thing I use from the legacy code is a std::tr1::shared_ptr object.

Why does the #include <tr1/functional> affect the non-tr1 std::function / std::bind? Or what's happening here?

Edit: According to GNU C++ Library Manual, Chapter 3: "A special case of the second rule is the mixing of TR1 and C++11 facilities. It is possible (although not especially prudent) to include both the TR1 version and the C++11 version of header in the same translation unit"

1

There are 1 best solutions below

0
T.C. On BEST ANSWER

There was a change in the standard that requires the implementation to constrain std::function's constructor so that it's not convertible from everything under the sun, but only from actual callables, so libstdc++ computes the return type with:

template<typename _Functor>
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
                         (std::declval<_ArgTypes>()...) );

and uses it in the SFINAE. Expression SFINAE means that if the call expression doesn't compile, the converting constructor is removed from the overload set.

The problem is with that __callable_functor. Its job is to wrap pointer-to-members so that they can be used with the normal function call syntax. And it's a reserved name so nobody else should be using it...well, except other components of the standard library. We have std::__callable_functor in <functional> and std::tr1::__callable_functor in <tr1/functional>. And because std::tr1 is an associated namespace (thanks to the use of std::tr1::shared_ptr<int> in the signature of Foo::test), you end up with an ambiguity.

Edit: reported as bug 68995.