在构造函数中反序列化一个 const 成员对象
Deserialize a const member object in a constructor
我需要在构造函数中初始化 class 的 const 成员对象,但该成员的构造函数只创建一个空对象,而真正的初始化必须通过从文件中反序列化该对象来完成。成员对象的class不是我的,改不了。也就是说,它是一个 Dlib 模型,下面的代码模拟了它的行为:
#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;
}
我不确定最好的方法是什么。首先想到的是使用 const_cast<>()
:
class FaceExtractor
{
public:
FaceExtractor()
{
std::cin >> const_cast<ShapePredictor&>(this->sp);
}
private:
const ShapePredictor sp;
};
它有效,但使用 const_cast<>()
通常被认为是一种不好的做法。我读到它主要是为了与不正确的遗留 API 兼容而设计的。我不太确定在我的情况下是否可以使用它。
解决它的另一种方法是创建一个成员函数 deserialize()
,它将 class 加载到一个临时对象中,然后 return 它:
class FaceExtractor
{
public:
FaceExtractor()
: sp(deserialize())
{ }
ShapePredictor deserialize()
{
ShapePredictor tmp;
std::cin >> tmp;
return tmp;
};
private:
const ShapePredictor sp;
};
这涉及创建临时文件,这是不可取的。希望 NRVO 会省略一个副本,但在 MSVC 中它仍然需要额外的一步。
我想知道初始化此类对象的常见做法是什么?
把成员变量设为非const
即可。拥有 const
个成员变量通常很痛苦,而且很少有用。
如果你绝对必须拥有它 const
,你可以创建一个继承自 ShapePredictor
的 class 并添加一个执行流式传输的构造函数。
struct ShapePredictorStreamable : public ShapePredictor {
ShapePredictorStreamable(std::istream& is) {
is >> *this;
}
};
class FaceExtractor {
public:
FaceExtractor() : sp(std::cin) {}
private:
const ShapePredictorStreamable sp;
};
我需要在构造函数中初始化 class 的 const 成员对象,但该成员的构造函数只创建一个空对象,而真正的初始化必须通过从文件中反序列化该对象来完成。成员对象的class不是我的,改不了。也就是说,它是一个 Dlib 模型,下面的代码模拟了它的行为:
#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;
}
我不确定最好的方法是什么。首先想到的是使用 const_cast<>()
:
class FaceExtractor
{
public:
FaceExtractor()
{
std::cin >> const_cast<ShapePredictor&>(this->sp);
}
private:
const ShapePredictor sp;
};
它有效,但使用 const_cast<>()
通常被认为是一种不好的做法。我读到它主要是为了与不正确的遗留 API 兼容而设计的。我不太确定在我的情况下是否可以使用它。
解决它的另一种方法是创建一个成员函数 deserialize()
,它将 class 加载到一个临时对象中,然后 return 它:
class FaceExtractor
{
public:
FaceExtractor()
: sp(deserialize())
{ }
ShapePredictor deserialize()
{
ShapePredictor tmp;
std::cin >> tmp;
return tmp;
};
private:
const ShapePredictor sp;
};
这涉及创建临时文件,这是不可取的。希望 NRVO 会省略一个副本,但在 MSVC 中它仍然需要额外的一步。
我想知道初始化此类对象的常见做法是什么?
把成员变量设为非const
即可。拥有 const
个成员变量通常很痛苦,而且很少有用。
如果你绝对必须拥有它 const
,你可以创建一个继承自 ShapePredictor
的 class 并添加一个执行流式传输的构造函数。
struct ShapePredictorStreamable : public ShapePredictor {
ShapePredictorStreamable(std::istream& is) {
is >> *this;
}
};
class FaceExtractor {
public:
FaceExtractor() : sp(std::cin) {}
private:
const ShapePredictorStreamable sp;
};