Can't copy a std::vector> using uniform i

2019-04-05 08:05发布

问题:

The following code does not compile in GCC 4.7.2 or Clang 3.2:

#include <vector>
#include <functional>

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

The issue is that the compiler will try to create b using an initializer_list, when clearly it should just be calling the copy constructor. However this seems to be desired behavior because the standard says that initializer_list constructors should take precedence.

This code would work fine for other std::vector, but for a std::function the compiler can't know whether you want the initializer_list constructor or another one.

It doesn't seem like there is a way around it, and if that is the case then you can never use uniform initialization in templated code. Which would be a giant shame.

Visual Studio (2012 November CTP) on the other hand doesn't complain about this. But the initializer_list support is not very good in there at the moment, so it might be a bug.

回答1:

This is LWG 2132 which is not yet a Defect Report but there's clear consensus (and implementation experience) to fix it. The standard says that std::function's constructor will accept any type, so because an initializer-list constructor is always preferred to other constructors if it's viable, your code tries to construct a vector from an std::initializer_list<std::function<void()>> with a single element initialized from the object a. That then causes an error because although you can construct a std::function<void()> from a the resulting object isn't callable.

In other words the issue is that std::function has an unconstrained template constructor allowing conversion from any type. That causes a problem in your case because initializer-list constructors are preferred to other constructors if viable, and the unconstrained function constructor means it's always possible to create an initializer_list<function<void()>> from any type so an initializer-list constructor is always viable.

The proposed resolution to 2132 prevents constructing a std::function from a non-callable type, so the initializer-list constructor isn't viable and the vector copy constructor is called instead. I implemented that resolution for GCC 4.8, and it's already implemented in Clang's libc++ library too.



回答2:

I can't see any reason why this shouldn't compile and both gcc (version 4.8.0 20121111) and clang (version 3.3 (trunk 171007)) compile the code. That said, "uniform initialization" is far from uniform: There are definitely cases where you can't use braces when invoking a constructor.