Contour based range with const shared_ptr <>

I have a container with shared_ptr<>, for example. a vector<shared_ptr<string>> v, and I would like to iterate vby specifying a constant.

This code:

vector<shared_ptr<string>> v;
v.push_back(make_shared<std::string>("hallo"));
...

for (const auto &s : v) {
    *s += ".";   // <<== should be invalid
}

It looks like what I want to do (this means that it sis const), but of course it does not create a string const.

Is there an elegant way to iterate over a container shared_ptrthat makes it clear that the content will not be modified?

Sort of

for (shared_ptr<const string> s : v) {
    *s += ".";   // <<== will not compile
}

(but this code will not compile for other reasons :))

Edit:

I made a mistake. I originally declared the link, which leads to a compiler error

for (shared_ptr<const string> &s : v) {   // <<== does not compile
    ...
}

shared_ptr<const string>, . , , .

+4
3

++, .

const ly, .

shared_ptr<string> shared_ptr<const string> .

1

for (const auto& ptr : v) {
    const auto& s = *ptr;

    s += ".";   // <<== is invalid
}

2

.

+9

.

:

, , . , , const, const , . pointee const, (, ) .

, () , unique_ptr shared_ptr pimpl. impl.

, ++ 17 propagate_const -.

:

#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;
}

:

hello
hello there

, operator*, . , , pointee const.

: http://en.cppreference.com/w/cpp/experimental/propagate_const

, shared_string, shared_ptr .

#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;
}

:

const hello
not const hello becomes hello there
const hello there
+2

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));
     }
  }
}

-, , , - , .

+1

Source: https://habr.com/ru/post/1622120/


All Articles