I have a map of int -> { basic types } I need to store.
I would like to simple create a struct { int f1, int f2; }; and store the values directly, constructing the struct in situ during the store. I do not expect there to be any repeated keys, so try_emplace seems ideal.
I wrote this code:
// mcve.cpp
#include <map>
#include <string>
struct various { int f1, f2; };
using map_t = std::map<int, various>;
void
example()
{
map_t dict;
//dict.try_emplace(1, 2);
dict.try_emplace(1, 1, 2);
//dict.try_emplace(1, {1, 2});
}
But none of these options work.
Using clang++ I get errors like this. (Version: clang version 5.0.1 (tags/RELEASE_501/final))
/opt/local/libexec/llvm-5.0/include/c++/v1/tuple:1365:7: error: no matching
constructor for initialization of 'various'
second(_VSTD::forward<_Args2>(_VSTD::get<_I2>(__second_args))...)
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### ... many lines ...
mcve.cpp:14:7: note: in instantiation of function template specialization
'std::__1::map<int, various, std::__1::less<int>,
std::__1::allocator<std::__1::pair<const int, various> > >::try_emplace<int,
int>' requested here
dict.try_emplace(1, 1, 2);
^
mcve.cpp:4:8: note: candidate constructor (the implicit copy constructor) not
viable: requires 1 argument, but 2 were provided
struct various { int f1, f2; };
^
mcve.cpp:4:8: note: candidate constructor (the implicit move constructor) not
viable: requires 1 argument, but 2 were provided
mcve.cpp:4:8: note: candidate constructor (the implicit default constructor) not
viable: requires 0 arguments, but 2 were provided
1 error generated.
Using g++ I get errors like this. (Version: g++ (MacPorts gcc7 7.2.0_0) 7.2.0):
In file included from /opt/local/include/gcc7/c++/bits/node_handle.h:40:0,
from /opt/local/include/gcc7/c++/bits/stl_tree.h:72,
from /opt/local/include/gcc7/c++/map:60,
from mcve.cpp:1:
/opt/local/include/gcc7/c++/tuple: In instantiation of 'std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {int&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {int&&, int&&}; long unsigned int ..._Indexes2 = {0, 1}; _T1 = const int; _T2 = various]':
### ... many lines ...
mcve.cpp:14:26: required from here
/opt/local/include/gcc7/c++/tuple:1652:70: error: no matching function for call to 'various::various(int, int)'
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
mcve.cpp:4:8: note: candidate: various::various()
struct various { int f1, f2; };
^~~~~~~
mcve.cpp:4:8: note: candidate expects 0 arguments, 2 provided
mcve.cpp:4:8: note: candidate: constexpr various::various(const various&)
mcve.cpp:4:8: note: candidate expects 1 argument, 2 provided
mcve.cpp:4:8: note: candidate: constexpr various::various(various&&)
mcve.cpp:4:8: note: candidate expects 1 argument, 2 provided
So, how can I efficiently store data into my map (ideally, using simple, readable code)?
Your first two attempts
dict.try_emplace(1, 2);
dict.try_emplace(1, 1, 2);
fail because you cannot use try_emplace, emplace etc. to initialize an aggregate from a list of values. Standard allocators construct the types in-place using () instead of {}, which of course will fail for aggregates. See the answers to this question.
The third attempt
dict.try_emplace(1, {1, 2});
fails because a braced-init-list is not an expression, and doesn't have a type, so template argument deduction fails to deduce it as various.
You can get try_emplace to work by specifying you're constructing a various instance
dict.try_emplace(1, various{1, 2});
Or add appropriate constructor(s) to various, which will allow the first two attempts to work.
However, given that you're working with a map containing builtin types, the easiest solution is to just use insert instead of emplacement.
dict.insert({1, {1, 2}});
The map::insert overload called above is std::pair<iterator,bool> insert(value_type&&), where value_type is pair<const int, various>. The nested braced-init-lists are deduced as value_type by the overload resolution rules in over.match.list, specifically the second bullet
If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class
Tand the argument list consists of the elements of the initializer list.
The pair(T1 const&, T2 const&) constructor is selected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With