3

I have a std::unordered_map<string, std::array<int, 2>>. What is the syntax of emplaceing a value into the map?

unordered_map<string, array<int, 2>> contig_sizes;
string key{"key"};
array<int, 2> value{1, 2};

// OK ---1
contig_sizes.emplace(key, value);

// OK --- 2
contig_sizes.emplace(key, std::array<int, 2>{1, 2});

// compile error --3
//contig_sizes.emplace(key, {{1,2}});

// OK --4 (Nathan Oliver)
// Very inefficient results in two!!! extra copy c'tor
contig_sizes.insert({key, {1,2}});

// OK --5
// One extra move c'tor followed by one extra copy c'tor
contig_sizes.insert({key, std::array<int, 2>{1,2}});

// OK --6 
// Two extra move constructors
contig_sizes.insert(pair<const string, array<int, 2>>{key, array<int, 2>{1, 2}});

I am using clang++ -c -x c++ -std=c++14 and clang 3.6.0

I tested the code in http://ideone.com/pp72yR

Addendum: (4) was suggested by Nathan Oliver in the answer below

Leo Goodstadt
  • 2,519
  • 1
  • 23
  • 23
  • 1
    `emplace` needs to deduce the type of its arguments, but `{{1,2}}` has no type (it's not an expression). – dyp Oct 06 '15 at 14:12

1 Answers1

5

From cppreference std::unordered_map::emplace is declared as

template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );

So it tries to deduce the types passed to it. This brings into play [temp.deduct.type] § 14.8.2.5

— A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have a type for which deduction from an initializer list is specified (14.8.2.1). [ Example:

template<class T> void g(T);  
g({1,2,3}); // error: no argument deduced for T

—end example ]

So no type is able to be deduced.

If you want to create objects on the fly then you would use the form:

contig_sizes.emplace(key, std::array<int, 2>{1, 2});

Or we can create a typedef

typedef pair<const string, array<my_class, 2>> pair_type;

And then we can have

contig_sizes.emplace(pair_type{key, {1, 2}});

You could also use std::unordered_map::insert which takes a pair of the key/value which can be constructed from a braced initializer list.

contig_sizes.insert({key, {1, 2}});
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • `unordered_map::insert` results in two extra copy c'tors (http://ideone.com/pp72yR). To reduce it down to only move c'tors, you seem to need this horrible syntax: `contig_sizes.insert(pair>{key, array{1, 2}});` – Leo Goodstadt Oct 22 '15 at 13:18
  • 1
    @LeoGoodstadt That can be solved with a simple `typedef` though. `typedef pair> pair_type;` and then `contig_sizes.insert(pair_type{key, {1, 2}});` – NathanOliver Oct 22 '15 at 13:25
  • @LeoGoodstadt I updated the answer to show a easy on the eyes `emplace()` use. – NathanOliver Oct 22 '15 at 13:30
  • I can't get it to remove the extra copy c'tor with the `typedef`. For some reason, I still need `contig_sizes.emplace(pair_type{key, std::array{1, 2}});`. :-( And that is still not as efficient as my original `contig_sizes.emplace(key, std::array{1, 2});`!!! I might as well `typedef` the value type: `typedef std::array{1, 2} value_type;` `contig_sizes.emplace(key, value_type{1,2});` – Leo Goodstadt Oct 22 '15 at 14:05
  • @LeoGoodstadt did you try `contig_sizes.emplace(pair_type{key, {1, 2}});`? – NathanOliver Oct 22 '15 at 14:08
  • Yes. Extra copy c'tor. See (http://ideone.com/pp72yR) ("Emplace with pair typedef"). This may be because `key` is a lvalue... – Leo Goodstadt Oct 22 '15 at 14:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93074/discussion-between-leo-goodstadt-and-nathanoliver). – Leo Goodstadt Oct 22 '15 at 14:14