4

In the following code, a non-const method of an object calls a const-method of the same object that returns a const-pointer to the object's field, and then this returned pointer is casted to a non-const version — it's a technique similar to one in this answer: Elegant solution to duplicate, const and non-const, getters? [duplicate]. However, since I put pointers into a complex data structure, I am not sure it will actually work. Will it?

class Struct {
private:
    Field f, g;

    std::map<std::string, const FieldBase*> const_fields_t;
    std::map<std::string, FieldBase*> fields_t;

public:
    const_fields_t get_fields() const {
        return const_fields_t { { "f", &f }, { "g", &g } };
   }

   fields_t get_fields() {
        const Foo* = this;
        fields_t result;

        foreach(auto& v : const_this->get_fields()) {
            result[v->first] = const_cast<FieldBase*>(v->second);
        }
        return result;
   }
};
Community
  • 1
  • 1
Joker_vD
  • 3,715
  • 1
  • 28
  • 42

1 Answers1

1

Yes, (I cleaned up your code a bit):

#include <string>
#include <functional>
#include <iostream>
#include <map>
using namespace std;

class FieldBase {public: virtual string get_data() = 0; };
class Field : public FieldBase { public: string data; virtual string get_data() { return data; } };

class Struct {
public:
    Field f, g;

    typedef std::map<std::string, const FieldBase*> const_fields_t;
    typedef std::map<std::string, FieldBase*> fields_t;

public:
    const_fields_t get_fields() const {
        cout << "const get_fields() called" << endl;
        return const_fields_t { { "f", &f }, { "g", &g } };
   }

   fields_t get_fields() {
        cout << "non-const get_fields() called" << endl;
        auto const_this = static_cast<const Struct*>(this);
        fields_t result;

        for(auto& v : const_this->get_fields()) {
            result[v.first] = const_cast<FieldBase*>(v.second);
        }
        return result;
   }
};

int main ()
{
    Struct a;
    a.f.data = "Hello";
    a.g.data = "World";

    Struct::fields_t obj = a.get_fields();

    cout << obj["f"]->get_data() << endl;
    cout << obj["g"]->get_data() << endl;

}

Live example

You basically have the const function act like a gateway to get the values you need and then cast away the constness. That will also work for your purpose since the pointers are going to be de-consted and stored in the new map.

Keep in mind that there might probably be a better solution not involving all the above copying around.

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • Well, one idea was to cache those two maps (call `get_fields()` in the constructor, store the results in two fields), but then the move/copy constructors become non-trivial. And since I intended `Struct` to be a base class (with `get_fields() const` being pure virtual), this may cause some problems... – Joker_vD Sep 02 '14 at 13:35
  • The root of all this silliness is that `std::map` and `std::map` are completely unrelated, unlike `V (*)(K)` and `const V (*)(K)`. – Joker_vD Sep 02 '14 at 13:37
  • In case I got it correctly and you're planning on using virtual functions by the constructor just pay attention to some stuff (http://stackoverflow.com/a/25562868/1938163) – Marco A. Sep 02 '14 at 13:39