Search code examples
c++stlc++20

emplace_back error using struct with base class members?


In the following example, I have a base and derived struct, each with a single member, and a vector of the derived type. When I try to push_back to the vector, it works as expected. However, I get a compilation error when trying to use emplace_back.

#include <vector>

struct Foo
{
    int foo{};
};

struct Bar : public Foo
{
    int bar{};
};

int main()
{
    std::vector<Bar> barVec;
    
    // Works
    barVec.push_back({2, 3});
    
    // Doesn't work
    //barVec.emplace_back(2, 3);

    return 0;
}
/usr/include/c++/11/bits/alloc_traits.h:518:28: error: no matching function for call to ‘construct_at(Bar*&, int, int)’
  518 |           std::construct_at(__p, std::forward<_Args>(__args)...);

...

/usr/include/c++/11/bits/stl_construct.h:96:17: error: could not convert ‘std::declval()’ from ‘int’ to ‘Foo’
   96 |     -> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                 |
      |                 int

If I wrap the first argument as a Foo however, it works.

    // Works
    barVec.emplace_back(Foo{2}, 3);

I kind of understand what's going on (something about how std::allocator_traits::construct works) but I'm confused about the specifics. What are the steps that push_back and emplace_back take to construct the object, and why does the original emplace_back fail?


Solution

  • Parenthesized aggregate initialization doesn't support brace elision. (§dcl.init)


    Bar b1(2,3); would fail.

    Bar b2(Foo(2),3), Bar b2(Foo{2},3), Bar b2({2},3)* would success


    *since there is no way to pass {2} (as braced-init-list) around, you cannot use it at vector::emplace_back either.