简体   繁体   English

使用 const shared_ptr<> 基于范围的 for 循环

[英]range based for loop with const shared_ptr<>

I have a container with shared_ptr<> , eg a vector<shared_ptr<string>> v and I'd like to iterate over v indicating const-ness.我有一个带有shared_ptr<>的容器,例如vector<shared_ptr<string>> v并且我想遍历v指示常量性。

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 .看起来像我想要做的(表明sconst )但当然它不会使字符串const

Is there an elegant way to iterate over a container of shared_ptr which makes clear that the content won't be modified?有没有一种优雅的方法来迭代shared_ptr的容器,这清楚地表明内容不会被修改?

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.如果您声明shared_ptr<const string>示例有效。 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.这是 C++ 的一个众所周知的限制,有些人认为这不是限制。

You want to iterate const ly, but an immutable pointer doesn't imply an immutable pointee.您想迭代const ly,但不可变指针并不意味着不可变指针。

The type shared_ptr<string> and the type shared_ptr<const string> are effectively unrelated.类型shared_ptr<string>和类型shared_ptr<const string>实际上是不相关的。

Option 1选项1

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

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

Option 2选项 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.要么,none 或两者都不是 const,而 const 指针只是意味着它不会指向不同的东西。 If the pointee is const, the object may not be changed through the (possibly non-const) pointer.如果指针是 const,则对象可能不会通过(可能是非常量)指针更改。

Having said that, we (I) often write value-semantic wrapper objects that use unique_ptr or shared_ptr as the pimpl.话虽如此,我们 (I) 经常编写使用unique_ptrshared_ptr作为 pimpl 的值语义包装器对象。 Often we wish to propogate the constness of the wrapper to impl.通常我们希望将包装器的常量性传播到 impl。

I believe c++17 will solve this with it's propagate_const pointer wrapper.我相信 c++17 将通过它的propagate_const指针包装器解决这个问题。

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.这个很简单,只支持operator*但是添加一套完整的operator很简单。 Note that I disable mutable access when the pointee is const.请注意,当指针为 const 时,我禁用了可变访问。

reference: http://en.cppreference.com/w/cpp/experimental/propagate_const参考: 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.只是为了好玩,这里有一个shared_string类的完整示例,它在内部使用shared_ptr并正确传播shared_string

#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.我在那里放了一个 lambda 函数,编译器可能无论如何都会内联它,所以这里没有性能损失。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM