将 std::tr1::shared_ptr 与 std::function / std::bind 混合会导致较新的 gcc 出现编译器错误
Mixing std::tr1::shared_ptr with std::function / std::bind causes compiler errors with newer gcc
我需要使用一些使用 std::tr1::shared_ptr
的旧遗留代码。我需要包含的 header 包含 #include <tr1/memory>
和 #include <tr1/functional>
。在我的代码中,我想使用 std::function
和 std::bind
,因为我们的编译器支持这些。
这适用于 gcc 4.6.3 和 4.7.3。在 4.9.2、5.1.x 和 5.2.0 中,这会导致编译器错误。这似乎是由于 <tr1/functional>
-include.
我写了一个重现这个问题的小例子:
#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,没有 #include <tr1/functional>
,编译正常。在我的本地机器上测试过。
gcc-4.6.3,加上 #include <tr1/functional>
,编译成功。在我的本地机器上测试过。
gcc-5.1,没有 #include <tr1/functional>
,编译成功:http://ideone.com/XrkAXT
gcc-5.1, with #include <tr1/functional>
, 编译错误: http://ideone.com/sRWQLn
我根本没有使用任何 std::tr1::function
或 std::tr1::bind
,我唯一使用的遗留代码是 std::tr1::shared_ptr
object.
为什么 #include <tr1/functional>
会影响非 tr1 std::function
/ std::bind
?或者这里发生了什么?
编辑:根据GNU C++ Library Manual, Chapter 3:“第二条规则的一个特例是 TR1 和 C++11 设施的混合。有可能(尽管不是特别谨慎)在同一个翻译单元"
中同时包含 header 的 TR1 版本和 C++11 版本
标准发生了变化,要求实现约束 std::function
的构造函数,以便它不能从阳光下的所有东西转换,而只能从实际的可调用对象转换,所以 libstdc++ 计算 return 输入:
template<typename _Functor>
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
(std::declval<_ArgTypes>()...) );
并在 SFINAE 中使用它。表达式 SFINAE 意味着如果调用表达式不编译,转换构造函数将从重载集中删除。
问题在于__callable_functor
。它的工作是包装指向成员的指针,以便它们可以与正常的函数调用语法一起使用。而且它是一个保留名称,所以其他人不应该使用它……好吧,标准库的其他组件除外。我们在 <functional>
中有 std::__callable_functor
,在 <tr1/functional>
中有 std::tr1::__callable_functor
。因为 std::tr1
是一个关联的命名空间(由于在 Foo::test
的签名中使用了 std::tr1::shared_ptr<int>
),你最终会产生歧义。
编辑:报告为bug 68995。
我需要使用一些使用 std::tr1::shared_ptr
的旧遗留代码。我需要包含的 header 包含 #include <tr1/memory>
和 #include <tr1/functional>
。在我的代码中,我想使用 std::function
和 std::bind
,因为我们的编译器支持这些。
这适用于 gcc 4.6.3 和 4.7.3。在 4.9.2、5.1.x 和 5.2.0 中,这会导致编译器错误。这似乎是由于 <tr1/functional>
-include.
我写了一个重现这个问题的小例子:
#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,没有 #include <tr1/functional>
,编译正常。在我的本地机器上测试过。
gcc-4.6.3,加上 #include <tr1/functional>
,编译成功。在我的本地机器上测试过。
gcc-5.1,没有 #include <tr1/functional>
,编译成功:http://ideone.com/XrkAXT
gcc-5.1, with #include <tr1/functional>
, 编译错误: http://ideone.com/sRWQLn
我根本没有使用任何 std::tr1::function
或 std::tr1::bind
,我唯一使用的遗留代码是 std::tr1::shared_ptr
object.
为什么 #include <tr1/functional>
会影响非 tr1 std::function
/ std::bind
?或者这里发生了什么?
编辑:根据GNU C++ Library Manual, Chapter 3:“第二条规则的一个特例是 TR1 和 C++11 设施的混合。有可能(尽管不是特别谨慎)在同一个翻译单元"
中同时包含 header 的 TR1 版本和 C++11 版本标准发生了变化,要求实现约束 std::function
的构造函数,以便它不能从阳光下的所有东西转换,而只能从实际的可调用对象转换,所以 libstdc++ 计算 return 输入:
template<typename _Functor>
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
(std::declval<_ArgTypes>()...) );
并在 SFINAE 中使用它。表达式 SFINAE 意味着如果调用表达式不编译,转换构造函数将从重载集中删除。
问题在于__callable_functor
。它的工作是包装指向成员的指针,以便它们可以与正常的函数调用语法一起使用。而且它是一个保留名称,所以其他人不应该使用它……好吧,标准库的其他组件除外。我们在 <functional>
中有 std::__callable_functor
,在 <tr1/functional>
中有 std::tr1::__callable_functor
。因为 std::tr1
是一个关联的命名空间(由于在 Foo::test
的签名中使用了 std::tr1::shared_ptr<int>
),你最终会产生歧义。
编辑:报告为bug 68995。