Deserialize a const member object in a constructor

Viewed 92

I need to initialize the const member object of a class in a constructor, but the member's constructor creates just an empty object, whereas real initialization has to be done by deserialization of this object from a file. The class of the member object is not mine and I can't change it. Namely, it's a Dlib model, the following code simulates it's behavior:

#include <iostream>
#include <string>

// not my class, can't be changed
class ShapePredictor 
{
    friend std::istream& operator >> (std::istream& stream, ShapePredictor&);

public:
    ShapePredictor() = default;
    ShapePredictor(const ShapePredictor& other) : data(other.data) { std::cout << "copy" << std::endl; }
    ShapePredictor(ShapePredictor&& other) : data(std::move(other.data)) { std::cout << "moved" << std::endl; }
private:
    std::string data;
};

// deserialization 
std::istream& operator >> (std::istream& stream, ShapePredictor& sp)
{
    sp.data = "test33";
    return stream;
}

class FaceExtractor
{
public:
    FaceExtractor()
    {
        std::cin >> this->sp;       // won't compile        
    }

private:
    const ShapePredictor sp;
};

int main(int argc, char* argv[])
{
    FaceExtractor extractor;
    return 0;
}

I am not sure what is the best way to do it. The first thing that springs to mind is using const_cast<>():

class FaceExtractor
{
public:
    FaceExtractor()
    {
        std::cin >> const_cast<ShapePredictor&>(this->sp);
    }

private:
    const ShapePredictor sp;
};

It works, but using const_cast<>() is normally considered a bad practice. I read it is mostly designed for compatibility with legacy APIs that are not const correct. I am not quite sure whether it would be ok to use it in my case.

Another way to tackle it is to create a member function deserialize() which would load the class into a temporary object and return it:

class FaceExtractor
{
public:
    FaceExtractor()
        : sp(deserialize())
    {   }

    ShapePredictor deserialize()
    {
        ShapePredictor tmp;
        std::cin >> tmp;
        return tmp;
    };

private:
    const ShapePredictor sp;
};

This involves creating a temporary, which is undesirable. Hopefully, NRVO will elide a copy, but in MSVC it still costs one additional move.

I am wondering what is the common practice to initialize such kind of objects?

1 Answers

Just make the member variable non-const. Having const member variables is usually a pain and is rarely useful.

if you absolutely must have it const, you could make a class that inherits from ShapePredictor and add a constructor that does the streaming.

struct ShapePredictorStreamable : public ShapePredictor {
    ShapePredictorStreamable(std::istream& is) {
        is >> *this;
    }
};

class FaceExtractor {
public:
    FaceExtractor() : sp(std::cin) {}

private:
    const ShapePredictorStreamable sp;
};
Related