Initialising std::shared_ptr<std::map<>> using braced-init
Asked Answered
F

6

8

I have the following shared_ptr to a map:

std::shared_ptr<std::map<double, std::string>>

and I would like to initialise it using braced-init. Is it possible?

I've tried:

std::string s1("temp");
std::shared_ptr<std::map<double, std::string>> foo = std::make_shared<std::map<double, std::string>>(1000.0, s1);

but that gives the following error when compiled using Xcode 6.3:

/usr/include/c++/v1/map:853:14: Candidate constructor not viable: no known conversion from 'double' to 'const key_compare' (aka 'const std::__1::less<double>') for 1st argument

I've tried other variations of the first parameter (1000.0) without success.

Can anyone help?

Flagging answered 6/4, 2016 at 8:32 Comment(4)
possible, but not pretty, std::shared_ptr<std::map<double, std::string>> foo = std::make_shared<std::map<double, std::string>>(std::initializer_list<std::map<double, std::string>::value_type>{{1000.0, s1}});Soulless
or just std::shared_ptr<std::map<double, std::string>> foo = std::make_shared<std::map<double, std::string>>(std::map<double, std::string>{{1000.0, s1}}); but still not niceSoulless
That works. I note the use of value_type. I see its just typedef pair<const Key, Type>. Why do I need that?Flagging
The thing is that one object you add inside is std::pair<T1, T2>, thus you need to put std::pair in braced init list. EDIT: of the map, of course.Lawhorn
B
10

std::map has an initializer-list constructor:

map (initializer_list<value_type> il,
     const key_compare& comp = key_compare(),
     const allocator_type& alloc = allocator_type());

We can create a map using this constructor quite easily:

std::map<double,std::string> m1{{1000.0, s1}};

To use it in make_shared, we need to specify which instantiation of initializer_list we're providing:

auto foo = std::make_shared<std::map<double,std::string>>
           (std::initializer_list<std::map<double,std::string>::value_type>{{1000.0, s1}});

That looks really clumsy; but if you need this regularly, you could tidy it up with aliases:

#include <string>
#include <map>
#include <memory>

std::string s1{"temp"};

using map_ds = std::map<double,std::string>;
using il_ds = std::initializer_list<map_ds::value_type>;

auto foo = std::make_shared<map_ds>(il_ds{{1000.0, s1}});

You might instead prefer to define a template function to wrap the call:

#include <string>
#include <map>
#include <memory>

template<class Key, class T>
std::shared_ptr<std::map<Key,T>>
make_shared_map(std::initializer_list<typename std::map<Key,T>::value_type> il)
{
    return std::make_shared<std::map<Key,T>>(il);
}

std::string s1{"temp"};
auto foo = make_shared_map<double,std::string>({{1000, s1}});
Badr answered 6/4, 2016 at 8:55 Comment(2)
You could make il_double_to_string an alias template so it would work with any map type. Something like this.Orji
In case it matters, I used GCC 4.8.2 to test the above.Badr
C
1

Your problem is that you didn't actually put any braces in your initializer. I needed the following to get it to work:

auto foo = std::make_shared<std::map<double, std::string> >(
                         std::map<double, std::string>({{1000.0, s1}})
           );

The double std::map<double, std::string> bugs me. It really ought to be able to work out one of them given the other ... but gcc 5.3.0 wouldn't play ball.

You will definitely need the double braces. (Once to say you are initializing a map, once to delimit each entry.)

Caddric answered 6/4, 2016 at 8:55 Comment(3)
That creates an initializer_list, constructs a map from it, then copies the map. It would be better to just pass the initializer_list directly to make_shared and avoid the map copy.Conium
Agree absolutely. (Although there is a fighting chance the map copy will be a move, so it's not horrendously expensive.) I couldn't get it to work.Caddric
... and Toby Speight has produced the right answer.Caddric
C
1

you can do it without std::make_shared:

std::shared_ptr<std::map<double,std::string>> ptr(new std::map<double,std::string>({{1000.0, "string"}}));
Ciera answered 6/4, 2016 at 9:2 Comment(0)
R
0

This is a refinement and generalization of the answer by Toby Speight.

template<class Container>
std::shared_ptr<Container>
make_shared_init_list(std::initializer_list<typename Container::value_type> il) {
    return std::make_shared<Container>(il);
}

Usage:

auto foo = make_shared_init_list<std::map<double,std::string>>({{1000, "hi"}});

The advantage is that it retains the usage of unique_ptr by accepting the map as the template parameter instead of the key and value types. It also works transparently with map, unordered_map, or any other container constructors that can accept initializer lists.

Rossie answered 15/7, 2022 at 19:22 Comment(0)
Q
-2

Someting similar to this should do it...

 std::string s1("temp");  

 std::map<double, std::string> *m = new std::map<double, std::string>{{100., s1}};

 auto foo = std::shared_ptr<std::map<double, std::string>>(m);

or as a oneliner

auto foo2 = std::shared_ptr<std::map<double, std::string>>(new std::map<double, std::string>{{100., s1}});

(Sorry, first missed the requirement for initializer list)

Quach answered 6/4, 2016 at 8:43 Comment(0)
Q
-4

Change the key's type.

double is a bad type for keys because there is no operator== for it and different byte sequences can represent the same floating point value.

Quasimodo answered 6/4, 2016 at 8:49 Comment(12)
This doesn't answer the question.Stephainestephan
There is an operator== for double - but you need to use it with care.Caddric
std::map doesn't care about operator== though, it doesn't use it.Conium
No, not always. If you insert known values into the map then the keys will have precisely those values. Floating point numbers are not indeterminate and subject to quantum fluctuations. The problem with floating point numbers as map keys is that NaN is not ordered w.r.t the other values.Conium
@JonathanWakely , it uses operator== or equivalent of it !(a<b) && !(b<a), depends on implementationQuasimodo
If you can tell it uses operator== rather than operator <, you aren't using a C++ compiler.Caddric
@JonathanWakely: "Precisely those values" - modulo the problem that 0.1 may not equal 1./10. particularly if the first is a literal, and the second is an expression involving two variables with the given values. But yes, it can be done with care.Caddric
@jonezq, no, it does not depend on implementation. std::map is not allowed to use operator==.Conium
@MartinBonner, that's why I said "known values" and that you will have "precisely those values". If you try to compare them to slightly different values then what I said doesn't hold. But if you insert 0.1 and search for 0.1 you will find it, even though the actual value is something close to but not equal to 0.1Conium
@JonathanWakely: Sorry, yes - I know you understood that. I was clarifying for less experienced readers.Caddric
@JonathanWakely, so you can guarantee that if i search for some double key, and find it, it will be actually same?Quasimodo
@jonezq: That's not what he said. If you create a map with key of 0.0 then you can search for negative zero and find the 0.0 element. Bad things will happen if you search for a NaN element (which is not ordered with respect to other values). If you create an element with key 0.1 (literal), and then search for literal 0.1, you will find it. If you search for (1.0/10.0) you may well not. If you do find an element, then the value you searched for will compare equal to the key.Caddric

© 2022 - 2024 — McMap. All rights reserved.