Std::Map Default Value

std::map default value

No, there isn't. The simplest solution is to write your own free template function to do this. Something like:

#include <string>
#include <map>
using namespace std;

template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}

int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}

C++11 Update

Purpose: Account for generic associative containers, as well as optional comparator and allocator parameters.

template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}

map int,int default values

As soon as you access the map with the [] operator, if the key doesn't exist it gets added. The int gets "value initialization" invoked - so it will get a value of 0.

Function to provide a default value when working with std::map

First of all you use operator[] on the map object inside the function. That will never be allowed because it is a non-const function and you have passed the map by const reference. Instead you should rewrite the function implementation to use iterators:

template<typename KeyType, typename ValueType>
ValueType mapDefaultInf(const std::map<KeyType, ValueType> & map, const KeyType & key)
{
const auto it = map.find(key);
return it != map.end() ? it->second : std::numeric_limits<ValueType>::infinity();
}

Second of all you have an ambiguity in the key type. You need to pass in a std::string. Like this:

auto el = mapDefaultInf(map, std::string("alexey"));

std::map default value (move only types)

This can be achieved with a proxy object.

template <typename T>
class PossiblyOwner
{
public:
struct Reference {};

PossiblyOwner(const PossiblyOwner & other)
: m_own(other.m_own),
m_ref(m_own.has_value() ? m_own.value() : other.m_ref)
{}
PossiblyOwner(PossiblyOwner && other)
: m_own(std::move(other.m_own)),
m_ref(m_own.has_value() ? m_own.value() : other.m_ref)
{}

PossiblyOwner(T && val) : m_own(std::move(val)), m_ref(m_own.value()) {}
PossiblyOwner(const T & val) : m_own(val), m_ref(m_own.value()) {}
PossiblyOwner(Reference, const T & val) : m_ref(val) {}
const T& value () const { return m_ref; }
operator const T& () const { return m_ref; }

// convenience operators, possibly also define ->, +, etc.
// but they are not strictly needed
auto operator *() const { return *m_ref; }
private:
std::optional<T> m_own;
const T & m_ref;
};

// Not strictly required
template <typename T>
std::ostream & operator<<(std::ostream & out,
const PossiblyOwner<T> & value)
{
return out << value.value();
}
template <typename Container, typename Key, typename ...DefaultArgs>
auto GetOrDefault(const Container & container, const Key & key,
DefaultArgs ...defaultArgs)
-> PossiblyOwner<decltype(container.find(key)->second)>
{
auto it = container.find(key);
using value_type = decltype(it->second);
using ret_type = PossiblyOwner<value_type>;
if (it == container.end())
return {value_type(std::forward<DefaultArgs>(defaultArgs)...)};
else
return {typename ret_type::Reference{}, it->second};
}

Then the usage can be:

int main()
{
std::map<int, std::unique_ptr<std::string>> mapping;
mapping.emplace(1, std::make_unique<std::string>("one"));
mapping.emplace(2, std::make_unique<std::string>("two"));
mapping.emplace(3, std::make_unique<std::string>("three"));
std::cout << *GetOrDefault(mapping, 0,
std::make_unique<std::string>("zero")) << "\n";
std::cout << *GetOrDefault(mapping, 1,
std::make_unique<std::string>("one1")) << "\n";
std::cout << *GetOrDefault(mapping, 3,
new std::string("three1")) << "\n";
}

Edit

I have noticed that the default copy constructor of PossiblyOwner<T> causes undefined behavior, so I had to define a non-default copy and move constructors.

Default value of static std::unordered_map

Yes, it is actually safe to assume that the values inside Foo are always initialized to zero because of the behaviour of operator[]

When the default allocator is used, this results in the key being copy/move constructed from key and the mapped value being value-initialized.

You do not provide a constructor which means that each field in Foo will be value initialized individually which for primitive types means zero initialization.

but

The problem you are actually facing here is that a field called "Apple" does not exist in your map. Unfortunately the semantics of operator[] are such that if the value does not exist, it will be created on the fly. You probably didn't even want to access a non-existent field in the map and you are asking whether it is always initialized to zero so that you can use this fact to check whether the element was there. For this purpose however, you should either use the find() or at() member function.

  • find() will return an iterator pointing to the end of the map if the element does not exist. That means you could guards the element access using

    if (auto apple = map.find("Apple"); apple != map.end()) {
    std::cout << apple->second.num << '\n';
    std::cout << apple->second.state << '\n';
    }

    (with the C++17 if statement initializer)

  • at() will throw an exception if the element is not found.

    std::cout << map.at("Apple").num << '\n';
    std::cout << map.at("Apple").state << '\n';

    This will crash your program with a std::out_of_range exception. You might feel temped to catch this exception to check whether the element existed. Don't do this. It is very bad practice to use exceptions for control flow. On top of that exception are dead slow when they are being thrown.

std::map default value for build-in type

This is defined in the standard, yes. map is performing "default initialization" in this case. As you say, for class types, that calls a no-arguments constructor.

For built-in types, in the '98 standard, see section 8.5, "Initializers":

To default-initialize an object of type T means:

  • if T is a non-POD ...
  • if T is an array type ...
  • otherwise, the storage for the object is zero-initialized

And, previously,

To zero-initialize storage for an object of type T means:

  • if T is a scalar type, the storage is set to the value 0 (zero) converted to T

Scalar types are:

  • Arithmetic types (integer, floating point)
  • Enumeration types
  • Pointer types
  • Pointer to member types

In particular, the behaviour you see with an integer (initialized to zero) is defined by the standard, and you can rely on it.

Default value of a pointer in a new std::map entry

If the key is not found in the map, the inserted value is value-initialized (§23.4.4.3/1). So no need for a wrapper; the pointer inserted will be a null pointer.

set default value of unordered map if key doesn't exist

One possibility would be to create a tiny class for the value type:

class dproxy {
double value_;
public:
dproxy(double value = std::numeric_limits<double>::max())
: value_{value} {}
operator double &() { return value_; }
operator double const &() const { return value_; }
};

std::unordered_map<Pair, dproxy> lFunction;

A default-constructed dproxy will be initialized to std::numeric_limits<double>::max(), and a dproxy will implicitly convert to a (reference to a) double. This can impose limitations if you're doing a lot of implicit conversions otherwise though (e.g., if you had some other function foo that took some other type bar that could be implicitly constructed from a double, foo(lFunction[baz]); would work if lFunction contained doubles, but not if it contained dproxy objects, since an implicit conversion sequence can only use one user-defined conversion).

Default value for the second element of the map STL?

what is the default value for the second element in map STL if I am initializing it with an array?

When accessing a key-value pair (kvp) in a std::map with operator[], either the key already exists, or a new kvp is constructed and the mapped_type is value-initialised. A value-initialized int is always 0. This imposes a requirement that it must be default constructible. Note that you can also access entries in a map using the at member function, which throws if the key is not found.

How can this program return the frequency of the element in the array by accessing the second element of map mp?

You have done this correctly in your code snippet. You could have used a std::multiset or std::unordered_multiset, they provide a count member function, that is the frequency of the key.

#include <set>
#include <iostream>

int main()
{
int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
std::multiset<int> freq (std::begin(arr), std::end(arr));

for(auto elem = freq.begin();
elem != freq.end();
elem=freq.upper_bound(*elem)) // Traverse the unique elements
{
std::cout << *elem << " count: " << freq.count(*elem) << "\n";
}
}

Godbolt


Note that your question mentions std::map but the example you provided references std::unordered_map, much of this applies to both data-structures.

std::map default value for enums

Will the initialization always be zero-initialization for enumerations? namely abc[0] is always initialized as the enumerator corresponding to 0?

Yes.

What if we have

enum X {
X1 = 1,
...

What will abc[0] be?

It will be 0.

Working program (also can be seen at http://ideone.com/RVOfT6):

#include <iostream>
#include <map>

enum X {
X1,
X2,
X3
};

int main()
{
X x = {};
std::map<int, X> abc;
std::cout << x << std::endl;
std::cout << abc[0] << std::endl;
}

Output:


0
0


Related Topics



Leave a reply



Submit