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
T
and 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