11

以下代码无法在 GCC 4.7.2 或 Clang 3.2 中编译:

#include <vector>
#include <functional>

int main()
{
   std::vector<std::function<void()>> a;
   std::vector<std::function<void()>> b{a};
}

问题是编译器将尝试使用 initializer_list 创建 b,而显然它应该只是调用复制构造函数。然而,这似乎是理想的行为,因为标准说 initializer_list 构造函数应该优先。

此代码适用于其他 std::vector,但对于 std::function,编译器无法知道您是否需要 initializer_list 构造函数或另一个构造函数。

似乎没有办法解决它,如果是这种情况,那么您永远不能在模板代码中使用统一初始化。这将是一个巨大的耻辱。

另一方面,Visual Studio(2012 年 11 月 CTP)并没有抱怨这一点。但是目前对 initializer_list 的支持不是很好,所以它可能是一个错误。

4

2 回答 2

11

这是LWG 2132,它还不是缺陷报告,但有明确的共识(和实施经验)来修复它。标准规定std::function' 的构造函数将接受任何类型,因此如果可行的话,初始化列表构造函数总是比其他构造函数更受欢迎,因此您的代码会尝试从std::initializer_list<std::function<void()>>具有从 object 初始化的单个元素的 a构造向量a。然后会导致错误,因为尽管您可以std::function<void()>a结果对象构造 a 是不可调用的。

换句话说,问题在于std::function有一个不受约束的模板构造函数,允许从任何类型进行转换。这会导致您的情况出现问题,因为如果可行,初始化列表构造函数比其他构造函数更受欢迎,并且不受约束的function构造函数意味着始终可以initializer_list<function<void()>>从任何类型创建一个,因此初始化列表构造函数始终是可行的。

提议的 2132 解决方案阻止std::function从不可调用的类型构造 a,因此初始化列表构造函数不可行,vector而是调用了复制构造函数。 我为 GCC 4.8 实现了该解决方案,并且它也已经在 Clang 的 libc++ 库中实现了。

于 2012-12-30T12:29:38.170 回答
5

我看不出任何不应该编译的原因,并且 gcc(版本 4.8.0 20121111)和 clang(版本 3.3(主干 171007))都编译了代码。也就是说,“统一初始化”远非统一:在调用构造函数时肯定存在不能使用大括号的情况。

于 2012-12-30T01:58:27.963 回答