12

I tried to initialize the std::vector

std::vector<Particle> particles;

with instances of the simple struct

struct Particle {
    int id;
    double x;
    double y;
    double theta;
    double weight;
};

by using emplace with an initializer list:

num_particles = 1000;
for (int i = 0; i < num_particles; i++)
{
    particles.emplace_back({ i,0.0,0.0,0.0,1 });
}

But I get the error

C2660 "std::vector>::emplace_back": Function doesn't accept one argument

How can I fix that?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Oblomov
  • 8,953
  • 22
  • 60
  • 106
  • 1
    Please elaborate on *Which doesn't work* – NathanOliver Apr 13 '17 at 14:24
  • 2
    You need to use `emplace_back`. The `emplace` function requires a position parameter. – TonyK Apr 13 '17 at 14:29
  • "Converting of argument 1 in from "initializer list" not possible" – Oblomov Apr 13 '17 at 14:29
  • Possible duplicate of [Brace-enclosed initializer list of templated struct](http://stackoverflow.com/questions/24493902/brace-enclosed-initializer-list-of-templated-struct). Does this solve your problem? – Veedrac Apr 13 '17 at 14:30
  • gcc tells you what the candidate constructor is, in the error message , `emplace(const_iterator __position, _Args&&... __args);` – M.M Apr 13 '17 at 14:31
  • @Veedrac that one is a different problem – M.M Apr 13 '17 at 14:31
  • @M.M I'm assuming `emplace` vs. `emplace_back` was just a typo. – Veedrac Apr 13 '17 at 14:32
  • @Veedrac well the code works (with some obvious furniture) once that change is made, so I think perhaps it wasn't a typo . OP should post a MCVE though. In your suggested duplicate, the problem is that the vector is of a template class (and template parameter cannot be deduced from initializer list), however in this question `Particle` is not a template. – M.M Apr 13 '17 at 14:33
  • @M.M Eh, that seems like drawing a difference where there is no practical distinction. The template isn't what makes the need to explicitly construct the argument. – Veedrac Apr 13 '17 at 14:41
  • OK. Actually it should be `push_back`, `emplace_back` cannot use aggregate initialization – M.M Apr 13 '17 at 14:42

3 Answers3

15

std::vector::emplace expects an iterator as argument too, because it inserts the element before that iterator's position.

Another problem is that your {i, 0.0, 0.0, 1} initialization doesn't work because it isn't in a context which tells what type it needs to instantiate. The reason there isn't any context is due to emplace and emplace_back member functions having generic parameters.

If you just want to append elements to the vector, use emplace_back.

However, emplace_back depends on the element type having a valid constructor in order to work, as the element is initialized through parentheses. That changed in C++20, which now allows aggregate-initialization through parentheses without the need to define a valid constructor.

So, up until C++17, your example would be changed to:

for (int i = 0; i < num_particles; ++i)
    particles.push_back({i, 0.0, 0.0, 1});

And in C++20 and later, you may do this instead:

for (int i = 0; i < num_particles; ++i)
    particles.emplace_back(i, 0.0, 0.0, 1);
Mário Feroldi
  • 3,463
  • 2
  • 24
  • 49
  • 17
    As soon as you have to construct an object of the type, you can well use `push_back` instead of `emplace_back`. The whole point of emplacing is to allow constructing the contained element in-place. – Angew is no longer proud of SO Apr 13 '17 at 14:38
10

You have several issues with your code:

  • Emplace takes an iterator as insertion point, and then a list of values which serve as arguments to a constructor.

  • Your struct must have a constructor which takes the values you pass to emplace.

  • You only have 4 argument values in your code, but your Particle struct has 5 fields.

Try this code instead:

struct Particle {
    int id;
    double x;
    double y;
    double theta;
    double weight;

    Particle(int id, double x, double y, double theta, double weight) 
        : id(id), x(x), y(y), theta(theta), weight(weight)
    {
    }
};

Notice the constructor there. And then emplace, for instance in the beginning [just an example which is not inserting at the back (see below)]:

std::vector<Particle> particles;

auto num_particles = 1000;
for (int i = 0; i < num_particles; i++)
{
    particles.emplace(particles.begin(), i, 0.0, 0.0, 1.0, 0.0);
}

As others have noted, if you just want to insert without specifying a specific position in the vector, you can use emplace_back:

std::vector<Particle> particles;

auto num_particles = 1000;
for (int i = 0; i < num_particles; i++)
{
    particles.emplace_back(i, 0.0, 0.0, 1.0, 0.0);
}

This inserts the elements at the end of the vector.

odyss-jii
  • 2,619
  • 15
  • 21
  • 2
    `Particle` is an aggregate so it needs no constructor. – NathanOliver Apr 13 '17 at 14:35
  • Thanks - I have fixed the parameter number issue. Is it possible to use initializer_list without a constructor? – Oblomov Apr 13 '17 at 14:35
  • 2
    @M.M @NathanOliver Why would you use `emplace` then? – odyss-jii Apr 13 '17 at 14:39
  • 1
    Well, if they want to write `particles.emplace_back(i, 0.0, 0.0, 1.0, 0.0);` then there must be a constructor. But OP seems to want to use a braced list, which doesn't require a constructor (although some other adjustment is necessary, as `emplace_back({.....})` is not legal). They could write `emplace_back(Particle{....});` although that has the same effect as push_back. Maybe it would clarify your answer to say *if* you want emplace_back without a braced list then you must have a constructor etc., but that's just one of the possible approaches – M.M Apr 13 '17 at 14:45
  • 1
    @M.M The entire purpose of `emplace` and `emplace_back` is to let `std::vector` construct the instance. If you plan on initializing it yourself and then passing it to the vector you should be using `insert` or `push_back`. If the original question relates to the usage of `emplace` then I think having a constructor is quite vital. – odyss-jii Apr 13 '17 at 14:48
4

First, std::vector::emplace requires the first argument passed to be an iterator representing the position where the element should be inserted.

Secondly, even if you provide the position. Template types are not deduced for initializer_lists. See initializer_list and template type deduction. So, below will equally fail:

particles.emplace( particles.end(), {i, 0.0, 0.0, 1, 1});

Since there is no constructor that can take the initializer list, below will likewise fail:

particles.emplace( particles.end(), i, 0.0, 0.0, 1, 1);

You either use insert or push_back as in:

particles.insert( particles.end(), {i, 0.0, 0.0, 1, 1});
particles.push_back({i, 0.0, 0.0, 1, 1});

or emplace or push_back:

particles.emplace( particles.end(), Particles{i, 0.0, 0.0, 1, 1});
particles.emplace_back(Particles{i, 0.0, 0.0, 1, 1});
Community
  • 1
  • 1
WhiZTiM
  • 21,207
  • 4
  • 43
  • 68