문제

I'm trying to create a wrapper around JsonCpp's Json::Value.

I have a namespace global where I have functions that work on Json::Value& parameters. I want to create a more syntactically pleasing wrapper class for those functions.

This is a minimal example.

// Impl is a typedef for Json::Value, from JsonCpp

namespace global
{
    Impl& getChild(Impl& mImpl, const std::string& mName) { return mImpl[mName]; }
    const Impl& getChildConst(const Impl& mImpl, const std::string& mName) { return mImpl[mName]; }

    Impl::iterator beginNonConst(Impl& mRoot)           { return mRoot.begin(); }
    Impl::iterator endNonConst(Impl& mRoot)             { return mRoot.end(); }
    Impl::const_iterator beginConst(const Impl& mRoot)  { return mRoot.begin(); }
    Impl::const_iterator endConst(const Impl& mRoot)    { return mRoot.end(); }
}

class Wrapper
{
    private:
        Impl& impl;

    public:
        Wrapper(Impl& mImpl) : impl(mImpl) { }

        Wrapper operator[](const std::string& mName) { return global::getChild(impl, mName); }

        // Error here
        // no known conversion for argument 1 from 'const Impl {aka const Json::Value}' to 'Impl& {aka Json::Value&}'
        const Wrapper operator[](const std::string& mName) const { return global::getChildConst(impl, mName); }

        Impl::iterator begin()              { return global::beginNonConst(impl); }
        Impl::iterator end()                { return global::endNonConst(impl); }
        Impl::const_iterator begin() const  { return global::beginConst(impl); }
        Impl::const_iterator end() const    { return global::endConst(impl); }
};

This is what I want to be able to compile:

int main()
{
    Json::Value realValue;
    Wrapper w(realValue)
    for(const auto& i : w["numberArray"]) { cout << i << endl; }
    for(auto& i : w["numberArray"]) { i += 100; }

    // assert that changes have been made to realValue through the wrapper

    return 0;
}
도움이 되었습니까?

해결책

A Wrapper has a member of type Impl&. Making the Wrapper object const only changes the top-level const of its members (which does nothing on a reference, references are already non-rebindable), and even that takes effect only after the constructor finishes.

You need a class with a member of type const Impl&, and const Wrapper does not. The compiler is correctly preventing you from losing the const qualifier and passing a const Impl& to Wrapper::Wrapper(Impl&) which could mutate its argument.

Generally const_iterator is a separate class from iterator. I see no reason why your Wrapper would be any different.

As a short-term solution, you can just use

const Wrapper operator[](const std::string& mName) const { return global::getChild(impl, mName); }

but this doesn't prevent anyone from copying the return value into a non-const Wrapper object and using it to mutate the Impl.

다른 팁

Isn't return type of both operator[] wrong? It should be Impl&/const Impl&, respectively?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top