Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't copy a std::vector<std::function<void ()>> using uniform initialization. Is this correct?

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.

like image 705
Malte Skarupke Avatar asked Dec 30 '12 01:12

Malte Skarupke


People also ask

What is uniform initialization in C++?

Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces ({}) to enclose initializer values.

Why use uniform initialization?

If your constructors merely copy their parameters in the respective class variables in exactly the same order in which they are declared inside the class, then using uniform initialization can eventually be faster (but can also be absolutely identical) than calling the constructor.

What is assignment and copy initialization?

Direct Initialization or Assignment Operator (Syntax) This assigns the value of one object to another object both of which are already exists. Copy initialization is used when a new object is created with some existing object. This is used when we want to assign existing object to new object.

What is std :: Initializer_list?

An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T .


2 Answers

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.

like image 96
Jonathan Wakely Avatar answered Nov 09 '22 05:11

Jonathan Wakely


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.

like image 5
Dietmar Kühl Avatar answered Nov 09 '22 05:11

Dietmar Kühl