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?