Can't insert non-const value in unordered_map of references - c++

I'm trying to create 2 std::unordered_map, one holds <A, int> and the second one holds <int&, A&>.
I'll explain at the end why I want to do this if you're curious.
My problem is that k_i has value of type std::reference_wrapper, k_i.insert doesn't work. But if I make k_i to have value std::reference_wrapper<const A>, the insert works.
I just can't figure out why is this and I am curious.
<<<<<Edit:
The thing is that find returns std::pair<const Ket, T> as stated by
Eljay in the comments. Because of this, the second std::unordered_map needs to have the value const.
<<<<<
Code:
Compiler: g++ version 10.1
Compile flags: -Wall -Wextra -std=c++20
#include <unordered_map>
#include <iostream>
#include <string>
#include <functional>
class A {
public:
A(const int x) : x(x) {
std::cout << "A::A(const int x) : x(" << x << ")\n";
}
A(const A& a) {
std::cout << "A::A {" << x << "} (const A& a {" << a.x << "} )\n";
x = a.x;
}
A(A&& a) {
std::cout << "A::A {" << x << "} (A&& a {" << a.x << "} )\n";
x = a.x;
}
A& operator=(const A& a) {
std::cout << "A::operator= {" << x << "} (const A& a)\n";
x = a.x;
return *this;
}
A& operator=(A&& a) {
std::cout << "A::operator= {" << x << "} (A&& a)\n";
x = a.x;
return *this;
}
~A() {
std::cout << "A::~A(" << x << ")\n";
}
friend std::ostream& operator<<(std::ostream& os, const A& dt);
int x;
};
std::ostream& operator<<(std::ostream& os, const A& dt) {
return os << dt.x;
}
template <typename K, typename V, typename... args>
void print_um(const std::unordered_map<K, V, args...> &umap) {
for (const auto &[x, y] : umap) {
std::cout << "(" << x << "," << std::ref(y).get() << "); ";
}
std::cout << "\n";
}
template <typename T>
struct MyHash {
std::size_t operator()(T const& s) const noexcept {
return std::hash<int>{}(std::ref(s).get());
}
};
template <typename T>
struct MyEquals {
constexpr bool operator()(const T &lhs, const T &rhs) const {
return lhs == rhs;
}
};
struct MyHash_A {
std::size_t operator()(A const& s) const noexcept {
return std::hash<int>{}(s.x);
}
};
struct MyEquals_A {
constexpr bool operator()(const A &lhs, const A &rhs) const {
return lhs.x == rhs.x;
}
};
int main() {
std::unordered_map<A, int, MyHash_A, MyEquals_A> k_s;
std::unordered_map<std::reference_wrapper<int>, std::reference_wrapper<const A>, MyHash<std::reference_wrapper<int>>, MyEquals<std::reference_wrapper<int>>> k_i;
{
A a(5);
std::cout << "1----\n";
k_s[a] = 12;
std::cout << "2----\n";
}
std::cout << "3----\n";
print_um<>(k_s);
std::cout << "4----\n";
A a(5);
std::cout << "5----\n";
auto it = k_s.find(a);
std::cout << "6----\n";
k_i.emplace((*it).second, (*it).first);
// // k_i[(*it).second] = ref_name;
std::cout << "7----\n";
print_um<>(k_s);
std::cout << "8----\n";
print_um<>(k_i);
std::cout << "9----\n";
int x = 12;
int &ref = x;
auto is_there = k_i.find(ref);
if (is_there != k_i.end()) {
std::cout << "elem: " << (*is_there).second.get() << "\n";
} else {
std::cout << "why? :(\n";
}
std::cout << "10---\n";
return 0;
}
As to why I create this code, I was thinking to be able to access some data by value or by key interchangeably (is there some better data structure? ). Like an username and a token, sometimes I have one, other times I have the other and using references I ensure that I don't waste space. Ofc, if one value has to change, I would invalidate the bucket position in the unordered_map because of the key, but I would treat that problem at a later date. Another motive is to learn some more C++ and test its limits (or mine).

From UnorderedAssociativeContainer requirements:
For std::unordered_map and std::unordered_multimap the value type is std::pair<const Key, T>.
In your code k_s is unordered_map<A, int>, so the value type is pair<const A, int>. In here:
auto it = k_s.find(a);
you get a "pointer" to such pair, and type of (*it).first is const A.
Your k_i is unordered_map<..., ref<A>> and when you do insert here:
k_i.emplace(..., (*it).first);
you essentially attempt to initialize ref<A> with const A, which obviously cannot work.
When you change k_i type to unordered_map<..., ref<const A>>, then you initialize ref<const A> with const A, which is fine.

Many have mentioned in the comment that Key type must be const. However the OP seems to wonder why the value type in k_i also need to be a const.
The reason for that is because you are referencing the key from k_s, which would be const A, and you can not reference it with a non-const reference.
To properly declare k_s, you might want to do something like:
std::unordered_map<
std::reference_wrapper<decltype(k_s)::value_type::second_type>,
std::reference_wrapper<decltype(k_s)::value_type::first_type>,
yourHash, yourComp
> k_s;
One alternative solution for you is to use another container that actually supports bidirectional lookup, such as Boost.Bimap.

Related

How can I print map key/value with std::variant?

I'm trying to print the key of a certain value in my dictionary. I defined my map like this:
std::map<std::string, std::variant<float,int,bool,std::string>> kwargs;
kwargs["interface"] = "probe";
kwargs["flag"] = true;
kwargs["Height"] = 5;
kwargs["length"] = 6;
I tried to print the value normally but a (no operator "<<" matches these operands) error occurs.
std::cout << kwargs["flag"] << std::endl;
Can someone help me with this error?
There's no operator<< for std::variant, which can't be printed out directly. You need to read the value from the variant (by index or type), e.g.
std::cout << std::get<bool>(kwargs["flag"]) << std::endl;
Or
std::cout << std::get<2>(kwargs["flag"]) << std::endl;
You can define operator<< for your type:
std::ostream& operator<<(std::ostream& os, std::variant<float,int,bool,std::string> const& v) {
std::visit([&os](auto const& x) { os << x; }, v);
return os;
}
You can generalize the solution to work with any variant specialization:
template <class Var, class = std::variant_alternative_t<0, Var>>
std::ostream& operator<<(std::ostream& os, Var const& v) {
std::visit([&os](auto const& x) { os << x; }, v);
return os;
}
Note, the second template parameter exists to disable the definition if the Var is not a std::variant specialization.
I would use std::visit with a generic lambda that prints any type to std::cout, as follows:
std::map<std::string, std::variant<float,int,bool,std::string>> kwargs;
kwargs["interface"] = "probe"s;
kwargs["flag"] = true;
kwargs["Height"] = 5;
kwargs["length"] = 6;
for (const auto& [k, v] : kwargs){
std::cout << k << " : ";
std::visit([](const auto& x){ std::cout << x; }, v);
std::cout << '\n';
}
This works because all of float, int, bool, and std::string have overloads for the output stream operator <<. For other types, or to override this behaviour, you could use a custom functor class instead of a lambda:
struct PrintVisitor {
template<typename T>
void operator()(const T& t) const {
std::cout << t;
}
// prints std::string in quotes
void operator(const std::string& s) const {
std::cout << '\"' << s << '\"';
}
};
[...]
std::visit(PrintVisitor{}, kwargs);
NOTE: unfortunately, the line kwargs["interface"] = "probe"; doesn't actually select the std::string constructor for the variant type, because the string literal preferentially converts to a bool rather than a std::string (further reading). You can avoid this by explicitly making a std::string using, for example, std::string{"probe"} or the standard std::string user-defined literal, as in "probe"s.
Live demo

Narrowing down a C++ concept to exclude certain types

Suppose I would want to overload the left shift operator for ostreams and all containers.
This is what I'm currently have (compile with -fconcepts):
#include <vector>
#include <iostream>
template<typename Container>
concept bool Iterable = requires(Container t) {
{ *t.begin()++, t.end() };
};
template<Iterable T>
std::ostream& operator<<(std::ostream& out, const T& t) {
for(const auto& it: t) {
out << it << " " ;
}
return out;
}
int main() {
std::vector<int> a = {1, 2, 3};
std::cout << a << std::endl;
std::string str = "something";
// std::cout << str << std::endl; // compile error if the template is defined
return 0;
}
The problem however, is that this there is already a version of the ostream&<< for std::string.
Is there a general (something like a requires not expression) or specific (maybe similar to partial specialization by which I can exclude concrete classes) way to exclude something in a concept?
If not, what is the correct way around this?
template<Iterable T>
requires !requires(std::ostream o, T a) { operator<<(o, a); }
std::ostream& operator<<(std::ostream& out, const T& t) {
for(const auto& it: t) {
out << it << " " ;
}
return out;
}
Add a requirement that the type does not already have an operator<< defined. I am not 100% sure this should work, but it does work on gcc.
(simply o << a crashes gcc)

shared_ptr that cannot be null?

Using a std::shared_ptr expresses shared ownership and optionality (with its possibility to be null).
I find myself in situations where I want to express shared ownership only in my code, and no optionality. When using a shared_ptr as a function parameter I have to let the function check that it is not null to be consistent/safe.
Passing a reference instead of course is an option in many cases, but I sometimes would also like to transfer the ownership, as it is possible with a shared_ptr.
Is there a class to replace shared_ptr without the possibility to be null, some convention to handle this problem, or does my question not make much sense?
You are asking for not_null wrapper class. Fortunately your issue is already addressed by C++ experts guideline and there are already example implementations - like this one. Search for not_null class template.
You could write a wrapper around std::shared_ptr that only allows creation from non-null:
#include <memory>
#include <cassert>
template <typename T>
class shared_reference
{
std::shared_ptr<T> m_ptr;
shared_reference(T* value) :m_ptr(value) { assert(value != nullptr); }
public:
shared_reference(const shared_reference&) = default;
shared_reference(shared_reference&&) = default;
~shared_reference() = default;
T* operator->() { return m_ptr.get(); }
const T* operator->() const { return m_ptr.get(); }
T& operator*() { return *m_ptr.get(); }
const T& operator*() const { return *m_ptr.get(); }
template <typename XT, typename...XTypes>
friend shared_reference<XT> make_shared_reference(XTypes&&...args);
};
template <typename T, typename...Types>
shared_reference<T> make_shared_reference(Types&&...args)
{
return shared_reference<T>(new T(std::forward<Types>(args)...));
}
Please note that operator= is missing yet. You should definitely add it.
You can use it like this:
#include <iostream>
using std::cout;
using std::endl;
struct test
{
int m_x;
test(int x) :m_x(x) { cout << "test("<<m_x<<")" << endl; }
test(const test& t) :m_x(t.m_x) { cout << "test(const test& " << m_x << ")" << endl; }
test(test&& t) :m_x(std::move(t.m_x)) { cout << "test(test&& " << m_x << ")" << endl; }
test& operator=(int x) { m_x = x; cout << "test::operator=(" << m_x << ")" << endl; return *this;}
test& operator=(const test& t) { m_x = t.m_x; cout << "test::operator=(const test& " << m_x << ")" << endl; return *this;}
test& operator=(test&& t) { m_x = std::move(t.m_x); cout << "test::operator=(test&& " << m_x << ")" << endl; return *this;}
~test() { cout << "~test(" << m_x << ")" << endl; }
};
#include <string>
int main() {
{
auto ref = make_shared_reference<test>(1);
auto ref2 = ref;
*ref2 = test(5);
}
{
test o(2);
auto ref = make_shared_reference<test>(std::move(o));
}
//Invalid case
//{
// test& a = *(test*)nullptr;
// auto ref = make_shared_reference<test>(a);
//}
}
Output:
test(1)
test(5)
test::operator=(test&& 5)
~test(5)
~test(5)
test(2)
test(test&& 2)
~test(2)
~test(2)
Example on Coliru
I hope I didn't forget anything that might result in undefined behaviour.
After taking a look at GSL's not_null class, which calls std::terminate() instead of abort();
Here is how I achieved it:
template <typename T>
class NonNull : public std::shared_ptr<T> {
typedef std::shared_ptr<T> super;
public:
inline NonNull()
: super(new T())
{
if ( ! super::get()) {
abort(); // Out of memory.
}
}
inline explicit NonNull(T *ptr)
: super(ptr)
{
if ( ! super::get()) {
abort(); // Input was null.
}
}
}
Basically, forces us to construct the class of T type.
Usage:
// Directly is a `std::shared_ptr` type:
NonNull<MyClass> myVariable;
// Unlike:
gsl::not_null<std::shared_ptr<MyClass > > myVariable;

boost::static_visitor multivisitor non-variant arguments

Is there any inexpensive way to pass an arguments of non-variant types in addition to arguments of variant types, when multivisitor applyed?
What I mean by the term "expensive way" is:
#include <boost/variant.hpp>
#include <iostream>
#include <cstdlib>
struct A {};
struct B {};
enum class C { X, Y };
std::ostream &
operator << (std::ostream & out, C const c)
{
switch (c) {
case C::X : {
return out << "C::X";
}
case C::Y : {
return out << "C::Y";
}
default : {
break;
}
}
throw std::runtime_error("unknown C value");
}
using V = boost::variant< A, B >;
struct S
: boost::static_visitor<>
{
void
operator () (C const c, A const &) const
{
std::cout << c << " A" << std::endl;
}
void
operator () (C const c, B const &) const
{
std::cout << c << " B" << std::endl;
}
};
int main()
{
V const a = A{};
V const b = B{};
using VC = boost::variant< C >;
VC const x = C::X;
VC const y = C::Y;
S const s;
boost::apply_visitor(s, x, a);
boost::apply_visitor(s, y, a);
boost::apply_visitor(s, x, b);
boost::apply_visitor(s, y, b);
return EXIT_SUCCESS;
}
Another expensive way is to make non-static visitor with fileds of required types (or references to required types) and construct instances of such visitor for each set of values of non-variant types every time.

Template specialization for a compile-time constant integer

Is it possible to specialize a function (or a class at least) to select between constant (compile-time!) integer and all other arguments? And if so, it would be nice to specialize (enable_if) for specific constant values only.
In the example below this would mean output "var", "const", and "var", not three "var"s.
#include <type_traits>
#include <iostream>
using namespace std;
struct test
{
template <typename T>
test& operator=(const T& var) { cout << "var" << endl; return *this; }
template <int n> // enable_if< n == 0 >
test& operator=(int x) { cout << "const" << endl; return *this; }
};
int main()
{
test x;
x = "x";
x = 1;
int y = 55;
x = y;
return 0;
}
UPDATE: edited the code to emphasize that it has to be compile-time constant.
To get var, const, var in your example you can do that.
struct test {
template <typename T>
test& operator=(const T& var) { cout << "var" << endl; return *this; }
test& operator=(int &&x) { cout << "const" << endl; return *this; }
};
It will work for all temporaries, but it will fail for:
const int yy = 55;
x = yy;
To make it work for such a case too you will need to add:
test& operator=(int &x) { cout << "var" << endl; return *this; }
test& operator=(const int &x) { cout << "const" << endl; return *this; }