当涉及 std::function 对象和 std::bind 时,我注意到 std::is_convertible 和 std::is_assignable 有一些奇怪的结果。
我假设当这些函数返回 true 时,可以进行转换。还是我遗漏了什么?
以下代码在不同的编译器上打印不同的结果,我希望它打印 0,因为这些类型无法分配。
#include <type_traits>
#include <functional>
#include <iostream>
int main()
{
std::cout << std::is_convertible<std::function<void(int)>, std::function<void()>>::value << std::endl;
}
它在以下编译器上打印 0:
- gcc 4.8 和 gcc 4.9
- clang 3.4(但不是来自 ubuntu 12.04 的那个)
它在以下编译器上打印 1:
- 海湾合作委员会 4.7
- VC++12 (VS2013)
- clang 3.2
有没有正确答案? 这些是编译器中的错误还是我弄乱了特定于编译器的东西?
最佳答案
在 C++11 中,std::function
采用任意仿函数类型的构造函数指定为(引用 N3337 §20.8.11.2.1 [func.wrap.func.con]/p7):
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.f
shall beCallable
(20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor ofA
shall not throw exceptions.
违反 Requires 子句(传递 f
而不是 Callable
参数类型 ArgTypes
和返回类型 R
)是未定义的行为,因此库可以自由地做任何事情在那种情况下它想要。该库可能会将构造函数从重载决议中移除,但它不是必须的,如果不是,那么你将遇到重载决议和 std::is_convertible
的问题。 - 它将报告几乎所有在阳光下的东西都可以转换为 std::function
(包括 double
之类的东西!)。
因此,在 LWG issue 2132 , 如果仿函数不是 Callable
,标准被修改为要求实现从重载解析中删除这些构造函数(通过 SFINAE 或类似技术)对于指定的参数类型和返回类型。现在是:
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.8 Remarks: These constructors shall not participate in overload resolution unless
f
isCallable
(20.9.11.2) for argument typesArgTypes...
and return typeR
.
所以如果你的标准库实现了这个解析,那么std::is_convertible<std::function<void(int)>, std::function<void()>>::value
是false
.否则,它取决于实现。
I would assume that when these functions return true, the conversion can be made. Or am I missing something?
类型特征,例如 std::is_convertible
, std::is_constructible
, 或 std::is_assignable
只考虑直接上下文——即是否存在可访问且未删除的匹配函数签名。它们不检查函数体在实例化时是否会编译。
关于c++ - std::is_convertible 与 std::function 不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25879865/