This question already has answers here:
no matching constructor for initialization of 'mapped_type' std::map error
(2 answers)
Closed 7 years ago.
When I define an argument to the constructor, its object can't be set to a map:
#include <map>
class Foo
{
public:
int fooInt;
Foo(int fooInt)
: fooInt(fooInt)
{};
};
int main()
{
Foo foo(2);
std::map<int, Foo> fooMap;
fooMap[1] = foo;
return 0;
}
// won't compile
If no argument (i.e. define Foo() and declare Foo foo), this works. Why?
I actually think I'm missing something very basic... but I can't figure out what is meant by the long error message.
This is because operator[] will insert a default constructed object if the key doesn't already exist in the map.
You're probably looking for insert for non-default-constructable object types.
std::map::operator[] requires the value type (or mapped_type) to be satisfy the DefaultInsertable concept. From [map.access]:
T& operator[](const key_type& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
Requires: key_type shall be CopyInsertable and mapped_type shall be DefaultInsertable into *this.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: Logarithmic
Foo declares its own constructor that disables the implicit generation of a default constructor, so it's not default-constructible.
The solution is to either declare a default constructor or use the emplace() method to construct a value type in-place:
fooMap.emplace(1, foo);
Related
This question already has answers here:
initializer_list and move semantics
(9 answers)
Closed 1 year ago.
I have a class that contains a unique_ptr. I want to place instances of this class inside a container (specifically std::map). This works using std::move and .emplace however, I would like to perform all this initialization within the container's initializer list. Is this possible?
I suspect Foo gets initialized in the initializer list then copied which is causing the problem. I've tried added a std::move in the initializer list but that hasn't solved the problem.
class Foo
{
public:
std::unique_ptr<std::string> my_str_ptrs;
}
Compilation Fails "attempting to access a deleted function". This is an example of what I want to do
std::map<std::string, Foo> my_map{
{"a", Foo{}}
};
Compilation Succeeds
std::map<std::string, Foo> my_map;
my_map.emplace("a", Foo{});
It's fault of std::initializer_list, if you look at it's begin/end member functions, they return const T*, which means it will force std::map to try use the copy constructor of your Foo, which is deleted as std::unique_ptr can not be copied.
This issue is not unique to std::map, any container which allows you to initialize it with std::initializer_list will really copy the arguments from an initializer list.
The C++ standard requires that during such initialization there's a temporary const T[N] array, which the std::initializer_list points to, const disables moves from it.
I have the following minimal example reproducing an error in my code:
#include <unordered_map>
#include <iostream>
class B
{
public:
B(int b) : m_b{ b } {}
int m_b;
};
int main()
{
using std::cout, std::endl;
std::unordered_map<int, B> ab{};
ab[1] = B(3);
//ab.insert(std::pair<int, B>(1, B(3)));
cout << ab[1].m_b << endl;
}
This fails with a long and unwieldy error which basically amounts to saying that there is no constructor for B without any arguments. The error stems from ab[1] = B(3) Why is that needed? And why does using insert instead of operator[] not need that constructor?
Bonus points for why this line in my original code:
Vec2 pos{ m_orbits[&p].positionAtTime(m_time + dt) };
also requires a non - parameterized constructor. I could not reproduce that error in my minimal example, but m_orbits is an unordered map with pointers to Particle objects as keys and Orbit objects as values. positionAtTime is a const member function of Orbit that calculates the position of a particle in the orbit at a certain time.
Why is [a constructor for B without any arguments] needed?
This is because std::map::operator[] required the mapped_type (i.e. in your case B) to be default constructable.
Inserts value_type(key, T()) if the key does not exist. This function is equivalent to return insert(std::make_pair(key, T())).first->second;
key_type must meet the requirements of CopyConstructible.
mapped_type must meet the requirements of CopyConstructible and DefaultConstructible.
If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.
When you provide a user defined constructor (i.e. B(int b)), the compiler will not generate a default constructor automatically, and thereby A can not be default construable.
If some user-declared constructors are present, the user may still force the automatic generation of a default constructor by the compiler that would be implicitly-declared otherwise with the keyword default.
Hence, the above error!
why does using insert instead of operator[] not need that constructor?
Because std::map::insert relies on the value_type (i.e. std::pair<const Key, T>). For your ab this is std::pair<const int, B>. From the cppreference.com the function overloads:
1-3) Inserts value. The overload (2) is equivalent to emplace(std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.
4-6) Inserts value in the position as close as possible, just prior(since C++11), to hint. The overload (5) is equivalent to emplace_hint(hint, std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.
So as long as B is constructable the std::pair<const int, B> also can be constructed and std::map::insert can be work.
I'm coming across a compiler error that says:
attempting to reference a deleted function
#include <iostream>
#include <vector>
template <typename T>
struct Container
{
Container() = default;
Container(const Container& other) = delete;
Container(T* ptr) : ptr(ptr) {}
T* ptr;
~Container() { delete ptr; }
};
struct Foo { Foo(int a, int b) {} };
int main()
{
std::vector<Container<Foo>> myvector;
myvector.push_back(new Foo(1, 2)); // I understand why this doesn't work.
myvector.emplace_back((new Foo(1, 2))); // I don't understand why this fails
}
I understand why it says attempting to reference a deleted constructor when I do std::vector::push_back(), because this does a copy and needs to call the copy constructor, which I deleted.
But std::vector::emplace_back() is supposed to take the constructor arguments of the type it holds. When I emplace back, I give it a pointer to a Foo, and this should be forwarded to the Container::Container(T* ptr) constructor.
What am I missing?
Declaring a User-Defined copy constructor will not define an implicit move constructor; T must either have a copy constructor or a move constructor to push_back or emplace_back* an object into a std::vector<T>.
From the docs, see the requirements on T to instantiate a std::vector<T>. (No restriction here, read on) ..emphasis mine
The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type meets the requirements of Erasable, but many member functions impose stricter requirements. This container (but not its members) can be instantiated with an incomplete element type if the allocator satisfies the allocator completeness requirements.
From std::vector<...>::push_back:
Type requirements
T must meet the requirements of CopyInsertable in order to use overload (1).
T must meet the requirements of MoveInsertable in order to use overload (2).
From std::vector<...>::emplace_back:
Type requirements
T (the container's element type) must meet the requirements of MoveInsertable and EmplaceConstructible.
For emplace_back here, your code would fulfill the EmplaceConstructible criteria, however, because reallcations can happen, you must equally fulfill MoveInsertable.
This question already has answers here:
C++ const std::map reference fails to compile
(5 answers)
Closed 6 years ago.
Ok here is the code snippet that I am having the problem with (class name has been changed due to work reason)
const std::map<A*, std::pair<int, B*> > &aMap = ot->getAMap();
A *a = getAFromSomewhere();
B* b = aMap[a].second; //The line that the compilation error points to.
Error: The operation "const std::map<A*, std::pair<int, B*>, std::less<A*>, std::allocator<std::pair<A*const, std::pair<int, B*>>>>[A*]" is illegal.
anyone has any idea why this is so?
The subscript operator for the class std::map is declared like
T& operator[](const key_type& x);
T& operator[](key_type&& x);
As you can see it is declared for non-constant objects of the class because if there is no object in the map with the given key then it is created by the operator.
And your object is constant due to the constant reference
const std::map<A*, std::pair<int, B*> > &aMap = ot->getAMap();
^^^^^
You should use member function at that is declared for constant objects.
const T& at(const key_type& x) const;
^^^^^
If you have an old compiler that does not support C++ 2011 then you can use member function find.
aMap is const, but std::map::operator[] is non-const member function (overloads), which can't be called on const object.
Use it with a non-const object will work, e.g.
std::map<A*, std::pair<int, B*> > aMap = ot->getAMap(); // aMap is non-const, copied from the returned map
A *a = getAFromSomewhere();
B *b = aMap[a].second;
Can a std::initializer_list contain reference types (both rvalue and lvalue)? Or does one have to use pointers or a reference wrapper (such as std::ref)?
EDIT:
Perhaps more clarification is due:
I have a member variable, ::std::vector<std::function<void()> >, into which I would like to forward a lambda object. This would usually be accomplished with emplace_back, but I wanted to do it in the constructor's initialization list. Alas, as I read, this would make forwarding impossible.
Can a std::initializer_list contain reference types (both rvalue and lvalue)?
std::initializer_list<T> doesn't hold references to its elements. It uses copy-semantics by holding its values as const objects:
18.9 Initializer List [support.initlist]
An object of type initializer_list<E> provides access to an array of objects of type const E.
An initializer_list of references will cause a compilation error because iternally pointers are used for iterators:
#include <initializer_list>
int main()
{
int x;
std::initializer_list<int&> l = {x};
// In instantiation of 'class std::initializer_list<int&>':
// error: forming pointer to reference type 'int&'
// typedef const _E* iterator;
}
An initializer_list also doesn't support move-semantics as const objects cannot be moved from. Holding your objects in a std::reference_wrapper<T> is the most viable solution if you wish to maintain reference-semantics.
From http://www.cplusplus.com/reference/initializer_list/initializer_list/
initializer_list objects are automatically constructed as if an array
of elements of type T was allocated
thus they can't be used with something like std::initializer_list<int&>. The reason is the same for which the following gives a compiler error
int& arr[20];
error: declaration of ‘arr’ as array of references
and that is dictated by the C++ standard: https://stackoverflow.com/a/1164306/1938163
You do not need list initialization here
As others mentioned, you cannot use std::initializer_list with references. You can use std::initializer_list<std::reference_wrapper<...>>, but it will prevent your from passing rvalues as arguments to the constructor, because std::reference_wrapper can only bind to lvalues. In other words, the following will not compile:
YourContainerOfFunctions C{ [](){} };
This makes usage of std::initializer_list in your case neither efficient nor convenient.
Use variadic templates instead!
I believe that is what you wanted to achieve:
class Foo {
std::vector<std::function<void()>> Functions;
public:
template <class... FuncTs>
Foo(FuncTs &&...Funcs) : Functions({std::forward<FuncTs>(Funcs)...}) {}
};
void foo(){};
int main() {
auto boo = []() {};
std::function<void()> moo = []() {};
Foo F{
foo, boo, // passed by reference, then copied
[]() {}, // moved, then copied
std::move(moo) // moved, then also moved
};
}
This requires at most one copy per argument, necessary because std::function always make a copy of functor object which it is constructed from. An exception is construction of std::function from std::function of the same type