range based for loop with const shared_ptr<> - c++

I have a container with shared_ptr<>, e.g. a vector<shared_ptr<string>> v and I'd like to iterate over v indicating const-ness.
This code:
vector<shared_ptr<string>> v;
v.push_back(make_shared<std::string>("hallo"));
...
for (const auto &s : v) {
*s += "."; // <<== should be invalid
}
looks like what I want to do (indicating that s is const) but of course it does not make the string const.
Is there an elegant way to iterate over a container of shared_ptr which makes clear that the content won't be modified?
Something like
for (shared_ptr<const string> s : v) {
*s += "."; // <<== will not compile
}
(but this code would not compile for other reasons :))
Edit:
I made a mistake. Originally I was declaring a reference, which results in a compiler error
for (shared_ptr<const string> &s : v) { // <<== does not compile
...
}
If you declare a shared_ptr<const string> the example works. In my eyes this is a good trade-off but this way the pointer gets copied which can be time consuming in loops with little code and big containers..

This is a well-known limitation of C++ that some don't consider to be a limitation.
You want to iterate constly, but an immutable pointer doesn't imply an immutable pointee.
The type shared_ptr<string> and the type shared_ptr<const string> are effectively unrelated.
Option 1
for (const auto& ptr : v) {
const auto& s = *ptr;
s += "."; // <<== is invalid
}
Option 2
Just don't modify it.

Here is the answer.
But first, the sermon:
A pointer and the thing it points to are two separate objects. Either, none or both may be const and a const pointer simply means that it will not point to a different thing. If the pointee is const, the object may not be changed through the (possibly non-const) pointer.
Having said that, we (I) often write value-semantic wrapper objects that use unique_ptr or shared_ptr as the pimpl. Often we wish to propogate the constness of the wrapper to impl.
I believe c++17 will solve this with it's propagate_const pointer wrapper.
In the meantime it's straightforward to build your own:
#include <iostream>
#include <type_traits>
#include <memory>
#include <string>
#include <vector>
namespace traits
{
template<class T> struct pointee;
template<class T, class D>
struct pointee<std::unique_ptr<T, D>> {
using type = T;
};
template<class T>
struct pointee<std::shared_ptr<T>> {
using type = T;
};
template<class T> using pointee_t = typename pointee<T>::type;
}
template<class PointerType>
struct propagate_const
{
using pointer_type = PointerType;
using element_type = traits::pointee_t<pointer_type>;
using value_type = std::decay_t<element_type>;
using reference = value_type&;
using const_reference = const value_type&;
propagate_const(pointer_type p) : _ptr(std::move(p)) {}
const_reference operator*() const {
return *_ptr;
}
auto operator*()
-> std::enable_if_t<not std::is_const<element_type>::value, reference>
{
return *_ptr;
}
private:
pointer_type _ptr;
};
template<class PointerType>
auto make_propagating_pointer(PointerType&& p)
{
return propagate_const<PointerType>(std::forward<PointerType>(p));
}
int main()
{
using namespace std;
vector<propagate_const<shared_ptr<string>>> v;
v.emplace_back(make_shared<string>("hello"));
for (const auto& p : v)
{
// *p += " there"; // compile error
cout << *p;
cout << endl;
}
for (auto& p : v)
{
*p += " there";
cout << *p;
cout << endl;
}
return 0;
}
expected output:
hello
hello there
This one is very simple, supporting only operator* but it's trivial to add a complete set of operators. Note that I disable mutable access when the pointee is const.
reference: http://en.cppreference.com/w/cpp/experimental/propagate_const
And just for fun, here's a complete example of a shared_string class that uses shared_ptr internally and propagates constness correctly.
#include <iostream>
#include <type_traits>
#include <memory>
#include <string>
#include <vector>
template<class PointerType>
struct propagate_const
{
using pointer_type = PointerType;
using element_type = std::remove_reference_t<decltype(*std::declval<PointerType&>())>;
using reference = element_type&;
using const_reference = const element_type&;
propagate_const(pointer_type p) : _ptr(std::move(p)) {}
const_reference operator*() const {
return *_ptr;
}
auto operator*()
-> std::enable_if_t<not std::is_const<element_type>::value, reference>
{
return *_ptr;
}
private:
pointer_type _ptr;
};
template<class PointerType>
auto make_propagating_pointer(PointerType&& p)
{
return propagate_const<PointerType>(std::forward<PointerType>(p));
}
struct shared_string
{
shared_string(std::string s) : _impl(std::make_shared<std::string>(std::move(s))) {};
shared_string(std::shared_ptr<std::string> sp) : _impl(sp) {};
shared_string(propagate_const<std::shared_ptr<std::string>> sp) : _impl(sp) {};
auto& operator += (const std::string& s) {
*_impl += s;
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const shared_string& ss) {
return os << *(ss._impl);
}
private:
propagate_const<std::shared_ptr<std::string>> _impl;
};
template<class T, std::enable_if_t<std::is_const<T>::value>* = nullptr >
std::string check_const(T&)
{
return std::string("const");
}
template<class T, std::enable_if_t<not std::is_const<T>::value>* = nullptr >
std::string check_const(T&)
{
return std::string("not const");
}
int main()
{
using namespace std;
// a vector of mutable shared_strings
vector<shared_string> v;
// a vector of immutable shared_strings
vector<const shared_string> cv;
// make a shared_string
v.emplace_back(make_shared<string>("hello"));
// refer to the *same one* in cv
cv.emplace_back(v[0]);
for (const auto& p : v)
{
// *p += " there"; // immutable reference to mutable shared string - not allowed
cout << check_const(p) << " " << p;
cout << endl;
}
for (auto& p : v)
{
cout << check_const(p) << " " << p;
p += " there"; // mutable reference to mutable shared string - allowed
cout << " becomes " << p;
cout << endl;
}
for (auto&p : cv)
{
cout << check_const(p) << " " << p;
// p += " world"; // p is actually immutable because cv contains immutable objects
cout << endl;
}
return 0;
}
expected output:
const hello
not const hello becomes hello there
const hello there

I would go with template approarch
template <class T,class F>
void forEach(const std::vector<std::shared_ptr<T>>& vec, F&& f){
for (const auto& ptr : vec){
if (ptr){
f(std::cref(*ptr));
}
}
}
I you put a lambda function there, the compiler will probably inline it anyway, so no performance damage here.

Related

Concept-restricted range-based for loop of std::list<std::reference_wrapper<T>>

I have some class Foo and a std::list<std::reference_wrapper<Foo>> and would like to iterate over its elements with a range-based for loop:
#include <list>
#include <functional>
#include <iostream>
class Foo {
public:
Foo(int a) : a(a) {}
int a;
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
for(Foo &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
Notice the additional get() when catching with auto, as we deduce type std::reference_wrapper<Foo>, whereas in the second case foo is already implicitly converted to type Foo& as we explicitly catch with this type.
I was actually looking for a way to catch with auto but implicitly cast away the std::reference_wrapper implicitly in order to not have to bother with the get() method all the time in the for body, so I tried introducing a fitting concept and catching with this, i.e. I tried
//this is not legal code
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
and hoped that it would work. clang however deduces the type of foo to std::reference_wrapper<Foo>, so that in fact below code will be correct:
//this compiles with clang, but not with gcc
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
return 0;
}
However, gcc completely refuses to accept the range-based for loop and complains deduced initializer does not satisfy placeholder constraints, as it tries to check LikeFoo<std::reference_wrapper<Foo>>, which of course evaluates to false, so with gcc one cannot even catch foo concept-restricted. Two questions arise:
Which of the compilers is correct? Should LikeFoo auto& foo : refs be valid?
Is there a way to auto-catch (possibly concept-restricted) foo : refs such that one can avoid having to write get() in the for-loop body?
You can find this example at the Compiler explorer.
Which of the compilers is correct? Should LikeFoo auto& foo : refs be valid?
No. refs is a range of reference_wrapper<Foo>&, so foo deduces to a reference to reference_wrapper<Foo> - which does not have a member named a. A constrained variable declaration doesn't change how deduction works, it just effectively behaves like an extra static_assert.
Is there a way to auto-catch (possibly concept-restricted) foo : refs such that one can avoid having to write get() in the for-loop body?
Just by writing refs? No. But you can write a range adaptor to convert your range of reference_wrapper<T> to a range of T&. There is already such a thing in the standard library, transform:
for (auto &foo : refs | std::views::transform([](auto r) -> decltype(auto) { return r.get(); })) {
That's a mouthful, so we can make it its own named adaptor:
inline constexpr auto unwrap_ref = std::views::transform(
[]<typename T>(std::reference_wrapper<T> ref) -> T& { return ref; });
And then you can write either:
for (auto &foo : refs | unwrap_ref) { ... }
for (auto &foo : unwrap_ref(refs)) { ... }
Either way, foo here deduces to be a Foo.
With a little bit more work, you can write a range adaptor that unwraps reference_wrapper<T> but preserves any other reference type.
Here is a bare minimum working example for a wrapper that calls get when being dereferenced.
#include <list>
#include <functional>
#include <iostream>
template <typename T>
struct reference_wrapper_unpacker {
struct iterator {
typename T::iterator it;
iterator& operator++() {
it++;
return *this;
}
iterator& operator--() {
it--;
return *this;
}
typename T::value_type::type& operator*() {
return it->get();
}
bool operator!=(const iterator& other) const {
return it != other.it;
}
};
reference_wrapper_unpacker(T& container) : t(container) {}
T& t;
iterator begin() const {
return {t.begin()};
}
iterator end() const {
return {t.end()};
}
};
class Foo {
public:
Foo(int a) : a(a) {}
int a;
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
for(Foo &foo : refs) {
std::cout << foo.a << std::endl;
}
for(auto &foo : reference_wrapper_unpacker{refs}) {
std::cout << foo.a << std::endl;
}
return 0;
}
To make it usable in generic code you would need to SFINAE to detect if the container actually has a reference_wrapper, and if not, just return the original container.
I'll leave that part out since it was not a part of the original question.

C++ replacement: Replacing every occurence of v[x] with v.at(x)

In C++, for a vector v, v.at(x) behaves like v[x], except that it throws an out of bounds error if a non-existent element is accessed.
I would like to ideally always use v.at(x), however, it is not as convenient to write as v[x]. Is there a way I can make v[x] behave like v.at(x), perhaps using something like #define?
If not, is there a better solution for out-of-bounds errors to always be thrown?
You may consider overloading the method in a new inheritance, as follows
#include <iostream>
#include <vector>
template< class T, class allocator = std::allocator<T>>
struct Vector : std::vector<T, allocator>{
using std::vector<T, allocator>::vector;
const T& operator[](size_t i)const{
return this -> at(i);
}
T& operator[](size_t i){
return this -> at(i);
}
};
template< class T>
Vector(size_t, T ) -> Vector<T>;//if u want to use c++17 deduction guides
int main()
{
std::vector<int> vec1(4,1);
std::cout << vec1[4];
Vector vec2(4,1);
std::cout << vec2[4];
}
You can use a proxy object with operator [] that calls method at(). Of course, this is not exactly what you want, but the syntax is similar: instead of v[x] you have to write at(v)[x].
Proof of concept:
#include <iostream>
#include <vector>
template<class T>
class AtProxy
{
public:
AtProxy(const AtProxy<T>& proxy) : m_obj{proxy.m_obj} {}
AtProxy(T& obj) : m_obj{obj} {}
typename T::reference operator [](size_t index)
{
return m_obj.at(index);
}
private:
T& m_obj;
};
template<class T>
AtProxy<T> at(T& v)
{
return AtProxy<T>(v);
}
int main()
{
try
{
std::vector<int> vec = {1,2,3,4,5};
for(size_t i = 0; i < 5; i++)
{
std::cout << i << std::endl;
at(vec)[i] = at(vec)[i + 1] + 1;
}
}
catch(std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
std::cout << "ok" << std::endl;
return 0;
}

Innocent range based for loop not working

The following does not compile:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto& s : {a, b, c, d}) {
s = 1;
}
std::cout << a << std::endl;
return 0;
}
Try it on godbolt
Compiler error is: error: assignment of read-only reference 's'
Now in my actual case the list is made of member variables on a class.
Now, this doesn't work because the expression becomes an initializer_list<int> that actually copies a,b,c, and d - hence also not allowing modification.
My question is two-fold:
Is there any motivation behind not allowing to write a range-based for loop in this way ? eg. perhaps there could be a special case for naked brace expressions.
What is a syntactical neat way of fixing this type of loop ?
Something along this line would be preferred:
for (auto& s : something(a, b, c, d)) {
s = 1;
}
I do not consider pointer indirection a good solution (that is {&a, &b, &c, &d}) - any solution should give the element reference directly when the iterator is de-referenced.
Ranges are not as magic as people would like. In the end, there must be an object that the compiler can generate calls on to either a member function or free function begin() and end().
Closest you'll probably be able to come is:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto s : {&a, &b, &c, &d} ) {
*s = 1;
}
std::cout << a << "\n";
return 0;
}
Just another solution within a wrapper idea:
template<typename T, std::size_t size>
class Ref_array {
using Array = std::array<T*, size>;
class Iterator {
public:
explicit Iterator(typename Array::iterator it) : it_(it) {}
void operator++() { ++it_; }
bool operator!=(const Iterator& other) const { return it_ != other.it_; }
decltype(auto) operator*() const { return **it_; }
private:
typename Array::iterator it_;
};
public:
explicit Ref_array(Array args) : args_(args) {}
auto begin() { return Iterator(args_.begin()); }
auto end() { return Iterator(args_.end()); }
private:
Array args_;
};
template<typename T, typename... Ts>
auto something(T& first, Ts&... rest) {
static_assert((std::is_same_v<T, Ts> && ...));
return Ref_array<T, 1 + sizeof...(Ts)>({&first, &rest...});
}
Then:
int main() {
int a{}, b{}, c{}, d{};
for (auto& s : something(a, b, c, d)) {
std::cout << s;
s = 1;
}
std::cout << std::endl;
for (auto& s : something(a, b, c, d))
std::cout << s;
}
outputs
0000
1111
According to the standard §11.6.4 List-initialization/p5 [dcl.init.list] [Emphasis Mine]:
An object of type 'std::initializer_list' is constructed from an
initializer list as if the implementation generated and materialized
(7.4) a prvalue of type “array of N const E”, where N is the number of
elements in the initializer list. Each element of that array is
copy-initialized with the corresponding element of the initializer
list, and the std::initializer_list object is constructed to refer
to that array. [ Note: A constructor or conversion function selected
for the copy shall be accessible (Clause 14) in the context of the
initializer list. — end note ] If a narrowing conversion is required
to initialize any of the elements, the program is ill-formed.
Thus, your compiler is complaining legitimately (i.e., auto &s deducts to int const& s and you cannot assign to s in the ranged for loop).
You could alleviate this problem by introducing a container instead of an initializer list (e.g., `std::vector’) with ‘std::reference_wrapper’:
#include <iostream>
#include <vector>
#include <functional>
int main()
{
int a{},b{},c{},d{};
for (auto& s : std::vector<std::reference_wrapper<int>>{a, b, c, d}) {
s.get()= 1;
}
std::cout << a << std::endl;
return 0;
}
Live Demo
To satisfy that syntax
for (auto& s : something{a, b, c, d}) {
s = 1;
}
you might create wrapper:
template <typename T>
struct MyRefWrapper
{
public:
MyRefWrapper(T& p) : p(&p) {}
T& operator =(const T& value) const { return *p = value; }
operator T& () const { return *p; }
private:
T* p;
};
Demo
Solution: use a reference wrapper
template <class It>
struct range_view_iterator : public It{//TODO: don't inherit It
auto& operator*() {
return (*this)->get();
}
};
template<class It>
range_view_iterator(It) -> range_view_iterator<It>;
template<class T>
struct range_view {
std::vector<std::reference_wrapper<T> > refs_;
range_view(std::initializer_list<std::reference_wrapper<T> > refs) : refs_{refs} {
}
auto begin() {
return range_view_iterator{ refs_.begin() };
}
auto end() {
return range_view_iterator{ refs_.end() };
}
};
Then used as:
for (auto& e : range_view<int>{a, b, c, d}) {
e = 1;
}
This doesn't try to answer the first question though.
You could create wrapper class for storing reference and which will have assignment operator to update this value:
template<class T>
struct Wrapper {
T& ref;
Wrapper(T& ref)
: ref(ref){}
template<class U>
void operator=(U u) {
ref = u;
}
};
template<class...T>
auto sth(T&...t) {
return std::array< Wrapper<std::common_type_t<T...> > ,sizeof...(t) >{Wrapper(t)...};
};
int main(){
int a{},b{},c{},d{};
for (auto s : sth(a,b,c,d)) {
s = 1;
}
std::cout << a << std::endl; // 1
Live demo

Is there a way to have a public member, unmodifiable from outside the class, without accessor wrapper function?

As far as I know, this seems to be impossible in a straightforward way. Making the member const makes it const for everyone. I would like to have a read-only property, but would like to avoid the typical "getter". I'd like const public, mutable private. Is this at all possible in C++?
Currently all I can think of is some trickery with templates and friend. I'm investigating this now.
Might seem like a stupid question, but I have been surprised by answers here before.
A possible solution can be based on an inner class of which the outer one is a friend, like the following one:
struct S {
template<typename T>
class Prop {
friend struct S;
T t;
void operator=(T val) { t = val; }
public:
operator const T &() const { return t; }
};
void f() {
prop = 42;
}
Prop<int> prop;
};
int main() {
S s;
int i = s.prop;
//s.prop = 0;
s.f();
return i, 0;
}
As shown in the example, the class S can modify the property from within its member functions (see S::f). On the other side, the property cannot be modified in any other way but still read by means of the given operator that returns a const reference to the actual variable.
There seems to be another, more obvious solution: use a public const reference member, pointing to the private, mutable, member. live code here.
#include <iostream>
struct S {
private:
int member;
public:
const int& prop;
S() : member{42}, prop{member} {}
S(const S& s) : member{s.member}, prop{member} {}
S(S&& s) : member(s.member), prop{member} {}
S& operator=(const S& s) { member = s.member; return *this; }
S& operator=(S&& s) { member = s.member; return *this; }
void f() { member = 32; }
};
int main() {
using namespace std;
S s;
int i = s.prop;
cout << i << endl;
cout << s.prop << endl;
S s2{s};
// s.prop = 32; // ERROR: does not compile
s.f();
cout << s.prop << endl;
cout << s2.prop << endl;
s2.f();
S s3 = move(s2);
cout << s3.prop << endl;
S s4;
cout << s4.prop << endl;
s4 = s3;
cout << s4.prop << endl;
s4 = S{};
cout << s4.prop << endl;
}
I like #skypjack's answer, but would have written it somehow like this:
#include <iostream>
template <class Parent, class Value> class ROMember {
friend Parent;
Value v_;
inline ROMember(Value const &v) : v_{v} {}
inline ROMember(Value &&v) : v_{std::move(v)} {}
inline Value &operator=(Value const &v) {
v_ = v;
return v_;
}
inline Value &operator=(Value &&v) {
v_ = std::move(v);
return v_;
}
inline operator Value& () & {
return v_;
}
inline operator Value const & () const & {
return v_;
}
inline operator Value&& () && {
return std::move(v_);
}
public:
inline Value const &operator()() const { return v_; }
};
class S {
template <class T> using member_t = ROMember<S, T>;
public:
member_t<int> val = 0;
void f() { val = 1; }
};
int main() {
S s;
std::cout << s.val() << "\n";
s.f();
std::cout << s.val() << "\n";
return 0;
}
Some enable_ifs are missing to really be generic to the core, but the spirit is to make it re-usable and to keep the calls looking like getters.
This is indeed a trickery with friend.
You can use curiously recurring template pattern and friend the super class from within a property class like so:
#include <utility>
#include <cassert>
template<typename Super, typename T>
class property {
friend Super;
protected:
T& operator=(const T& val)
{ value = val; return value; }
T& operator=(T&& val)
{ value = val; return value; }
operator T && () &&
{ return std::move(value); }
public:
operator T const& () const&
{ return value; }
private:
T value;
};
struct wrap {
wrap() {
// Assign OK
prop1 = 5; // This is legal since we are friends
prop2 = 10;
prop3 = 15;
// Move OK
prop2 = std::move(prop1);
assert(prop1 == 5 && prop2 == 5);
// Swap OK
std::swap(prop2, prop3);
assert(prop2 == 15 && prop3 == 5);
}
property<wrap, int> prop1;
property<wrap, int> prop2;
property<wrap, int> prop3;
};
int foo() {
wrap w{};
w.prop1 = 5; // This is illegal since operator= is protected
return w.prop1; // But this is perfectly legal
}

C++ - Map of Vectors of Smart Pointers - All inherited from the same base class

I've got this Map in my Entity-Component-System:
std::map<u_int32_t, std::vector<std::shared_ptr<Component>>> _componentMap;
The u_int32_t is the key to a vector of components. There can be multiple instances of the same component. (That's why there's a vector).
Now I would like to have a templated getter-function that returns a Vector of an inherited type:
template<class T> inline const std::vector<std::shared_ptr<T>> & getVector() const
{
u_int32_t key = getKey<T>();
return static_cast<std::vector<std::shared_ptr<T>>>(_componentMap.count(key) ? _componentMap.at(key) : _emptyComponentVec);
}
I know that this doesn't work, since std::vectors of different types are completely unrelated and I cannot cast between them. I would also like to avoid allocating a new vector every time this function is called.
But how I can I get the desired behaviour? When the the components are added I can create an std::vector of the desired derived type.
The question could also be: How can I have an std::map containing different types of std::vector?
For any solutions I can not link against boost, though if absolutely needed, I could integrate single headers of boost.
template<class It>
struct range_view {
It b, e;
It begin() const { return b; }
It end() const { return e; }
using reference = decltype(*std::declval<It const&>());
reference operator[](std::size_t n) const
{
return b[n];
}
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
reference front() const {
return *begin();
}
reference back() const {
return *std::prev(end());
}
template<class O>
range_view( O&& o ):
b(std::begin(o)), e(std::end(o))
{}
};
this is a quick range view. It can be improved.
Now all you need to do is write a pseudo-random-access iterator that converts its arguments. So it takes a random access iterator over a type T, then does some operation F to return a type U. It forwards all other operations.
The map then stores std::vector<std::shared_ptr<Base>>. The gettor returns a range_view< converting_iterator<spBase2spDerived> >.
Here is a crude implementation of a solution I have in mind for this problem. Of course, there are many rooms to refine the code, but hopefully it conveys my idea.
#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;
class Base {
public:
virtual void f() const = 0;
};
class A : public Base {
public:
static const int type = 0;
explicit A(int a) : a_(a) {}
void f() const { cout << "calling A::f" << endl;}
int a_;
};
class B : public Base {
public:
static const int type = 1;
explicit B(int a) : a_(a) {}
void f() const { cout << "calling B::f" << endl;}
int a_;
};
class MapWrapper {
public:
template<class T>
void append(int a, vector<T> const& vec) {
types_[a] = T::type;
my_map_[a] = make_shared<vector<T>>(vec);
}
template<class T>
vector<T> const& get(int a) const {
return *static_pointer_cast<vector<T>>( my_map_.at(a) );
}
map<int, shared_ptr<void>> const& get_my_map() const {
return my_map_;
}
vector<shared_ptr<Base>> get_base(int a) const {
vector<shared_ptr<Base>> ret;
switch(types_.at(a)) {
case 0: {
auto const vec = get<A>(a);
for(auto v : vec)
ret.push_back(make_shared<A>(v));
break;
}
case 1: {
auto const vec = get<B>(a);
for(auto v : vec)
ret.push_back(make_shared<B>(v));
break;
}
}
return ret;
}
map<int, shared_ptr<void>> my_map_;
map<int, int> types_;
};
int main() {
MapWrapper map_wrapper;
map_wrapper.append(10, vector<A>{A(2), A(4)});
map_wrapper.append(20, vector<B>{B(5), B(7), B(9)});
for(auto const& w : map_wrapper.get_my_map())
for(auto v : map_wrapper.get_base(w.first))
v->f();
for(auto const& x: map_wrapper.get<A>(10))
cout << x.a_ << " ";
cout << endl;
for(auto const& x: map_wrapper.get<B>(20))
cout << x.a_ << " ";
return 0;
}
The solution was to use reinterpret_cast:
template<class T> inline std::vector<std::shared_ptr<T>> * getVector() const
{
auto key = getKey<T>();
return reinterpret_cast<std::vector<std::shared_ptr<T>> *>( (_componentMap.count(key) ? _componentMap.at(key).get() : const_cast<std::vector<std::shared_ptr<Component>> *>(&_emptyComponentSharedPtrVec)) );
}
It's not very pretty but it does work fine and it fulfills all requirements.