设计一个文字类型class,里面有一个变体字段,里面可以存储一个或三个对象
Designing a literal-type class with a variant field inside in which one or three objects may be stored
我正在尝试设计一个 class - 为了便于讨论,我们称之为 A
- 它将满足一组特定的要求:
A
必须是文字类型,以允许编译器在编译时通过 constexpr
构造函数初始化其全局实例(许多这种类型的全局 const
对象是在源代码)。这样做的原因是 A
在整数上采用了简单的编译时加密(主要是 XOR)。解密稍后在运行时完成,同时访问适当的 int
.
- 它的中央私有字段是一个简单的整数。但是,class 有两个构造函数:
A::A(int x)
和 A::A(int x, int y, int z)
。如果调用第一个版本,那么稍后,在运行时,只要进行需要使用它的方法调用,class 就会在内部使用该单个 x
。相反,如果使用具有三个参数的第二个版本,则在调用需要一个的函数时将在运行时决定使用x
、y
和z
中的哪一个。
A
必须是单一类型;第一个构造函数的一种类型和第二个构造函数的另一种类型并不令人满意(A
经常作为参数传递给函数,因此如果没有此约束,我将需要复制或模板化所有这些函数)。
- 绝大部分
A
对象都会是全局常量,很少会发生赋值,即使发生也肯定不会在三个int
的对象和一个对象之间与一个 int
反之亦然。
总结一下:A
将被实例化为全局 const
对象。如果我用单个 int
初始化一个对象,那么单个 int
应该存储在其中(仅此而已)。如果我用三个 int
初始化它,那么这三个 int
应该存储在里面。无需担心从三 int
对象到一个 int
对象的赋值,反之亦然,因为它们都是常量。
目前我考虑的解决方案如下:
制作A
模板;模板参数就是我所说的 StorageType
。该存储将通过公开访问器来抽象对中央 int
资源的访问。选择使用哪个 int
字段的问题将从 A
转移到此辅助存储类型。这个想法大致可以用这段代码来说明:
template<typename StorageType>
class A
{
private:
StorageType storage;
public:
constexpr A(int x, int y, int z) :
storage(x, y, z)
{ }
constexpr A(int x) :
storage(x)
{ }
void doSomething()
{
auto theRightInt = storage.getInt();
// ...
}
};
问题: 违反约束 3.
和以前一样,但不是在 StorageType
上模板化 A
,而是有一个通用接口来描述 StorageType
并存储 unique_ptr
到 A
.
里面的一个
问题: 违反约束 1.
将整数存储为并集:
union
{
struct
{
int x;
int y;
int z;
} intPack;
int singleInt;
};
在此变体中,每个 A
对象(包括仅使用单个 int
的对象)都可以容纳三个可能的 int
。另一种选择是使用 boost::variant
而不是过时的 union
.
问题:这很浪费 - 见第 4 点。如果使用 boost::variant
,它违反约束 1,因为 boost::variant
,不像 std::variant
] 来自 C++17,不是文字类型。
与其尝试在 [=14= 内表示中央整数字段的 "variance",不如在外部表示:让 class 始终存储单个 int
,并创建一个助手 class - 我们称它为 VariantA
- 包含三个版本的 A
,每个版本都用 x
、y
初始化和第 2 点中提到的构造函数中的 z
。VariantA
将具有一个访问函数,该函数在运行时决定哪个 A
对象 return.
问题:使用起来很乏味,因为每次都必须调用访问器:
VariantA a1(0, 1, 2);
VariantA a2(10, 20, 30);
auto a3 = a1.get() + a2.get();
someFunctionThatTakesAnA(a1.get());
// etc
问题:
对于我错过的这个问题,是否有一个优雅的解决方案,或者我是否必须选择我在上面考虑过(并拒绝)的解决方案之一?
我的平台是 VC++,所以使用 C++11/4 是可以的,除了一些更深奥的部分,但是 C++17 草稿中的功能还不可用。
有 2 个成员的 class/struct?
指向 int
和 bool
的指针(布尔值表示指针指向一个或三个值)?
#include <iostream>
struct foo
{
bool single;
int const * p;
constexpr foo ( int const & x ) : single{true}, p{&x}
{}
constexpr foo ( int const * y ) : single{false}, p{y}
{}
};
constexpr int x{2};
constexpr int y[]{3, 5, 7};
int main ()
{
constexpr foo f1{x};
constexpr foo f3{y};
}
似乎您可以使用的最好的东西是有条件的整数范围:
class A {
std::array<int, 3> data;
bool one;
public:
constexpr A(int x): data{{x, 0, 0}}, one(true) { }
constexpr A(int x, int y, int z): data{{x, y, z}}, one(false) { }
constexpr size_t size() const { return one ? 1 : 3; }
constexpr const int* begin() const { return data.data(); }
constexpr const int* end() const { return data.data() + size(); }
};
我不完全确定你选择哪个元素的逻辑是什么,但如果你有一个 1 尺寸的范围,你会得到那个元素,如果你有一个 3 尺寸的范围,那么你会做任何事情你需要做的。
不管具体细节如何,重点是你想要一个有3个int
和一个bool
的类型。您需要存储 3 int
,您希望它是字面值,并且您希望它知道它存储的是 3 还是 1 int
。这几乎说明了类型。剩下的唯一事情就是确定您希望数据访问的外观如何,而这并没有指定。
我正在尝试设计一个 class - 为了便于讨论,我们称之为 A
- 它将满足一组特定的要求:
A
必须是文字类型,以允许编译器在编译时通过constexpr
构造函数初始化其全局实例(许多这种类型的全局const
对象是在源代码)。这样做的原因是A
在整数上采用了简单的编译时加密(主要是 XOR)。解密稍后在运行时完成,同时访问适当的int
.- 它的中央私有字段是一个简单的整数。但是,class 有两个构造函数:
A::A(int x)
和A::A(int x, int y, int z)
。如果调用第一个版本,那么稍后,在运行时,只要进行需要使用它的方法调用,class 就会在内部使用该单个x
。相反,如果使用具有三个参数的第二个版本,则在调用需要一个的函数时将在运行时决定使用x
、y
和z
中的哪一个。 A
必须是单一类型;第一个构造函数的一种类型和第二个构造函数的另一种类型并不令人满意(A
经常作为参数传递给函数,因此如果没有此约束,我将需要复制或模板化所有这些函数)。- 绝大部分
A
对象都会是全局常量,很少会发生赋值,即使发生也肯定不会在三个int
的对象和一个对象之间与一个int
反之亦然。
总结一下:A
将被实例化为全局 const
对象。如果我用单个 int
初始化一个对象,那么单个 int
应该存储在其中(仅此而已)。如果我用三个 int
初始化它,那么这三个 int
应该存储在里面。无需担心从三 int
对象到一个 int
对象的赋值,反之亦然,因为它们都是常量。
目前我考虑的解决方案如下:
制作
A
模板;模板参数就是我所说的StorageType
。该存储将通过公开访问器来抽象对中央int
资源的访问。选择使用哪个int
字段的问题将从A
转移到此辅助存储类型。这个想法大致可以用这段代码来说明:template<typename StorageType> class A { private: StorageType storage; public: constexpr A(int x, int y, int z) : storage(x, y, z) { } constexpr A(int x) : storage(x) { } void doSomething() { auto theRightInt = storage.getInt(); // ... } };
问题: 违反约束 3.
和以前一样,但不是在
里面的一个StorageType
上模板化A
,而是有一个通用接口来描述StorageType
并存储unique_ptr
到A
.问题: 违反约束 1.
将整数存储为并集:
union { struct { int x; int y; int z; } intPack; int singleInt; };
在此变体中,每个
A
对象(包括仅使用单个int
的对象)都可以容纳三个可能的int
。另一种选择是使用boost::variant
而不是过时的union
.问题:这很浪费 - 见第 4 点。如果使用
boost::variant
,它违反约束 1,因为boost::variant
,不像std::variant
] 来自 C++17,不是文字类型。与其尝试在 [=14= 内表示中央整数字段的 "variance",不如在外部表示:让 class 始终存储单个
int
,并创建一个助手 class - 我们称它为VariantA
- 包含三个版本的A
,每个版本都用x
、y
初始化和第 2 点中提到的构造函数中的z
。VariantA
将具有一个访问函数,该函数在运行时决定哪个A
对象 return.问题:使用起来很乏味,因为每次都必须调用访问器:
VariantA a1(0, 1, 2); VariantA a2(10, 20, 30); auto a3 = a1.get() + a2.get(); someFunctionThatTakesAnA(a1.get()); // etc
问题: 对于我错过的这个问题,是否有一个优雅的解决方案,或者我是否必须选择我在上面考虑过(并拒绝)的解决方案之一? 我的平台是 VC++,所以使用 C++11/4 是可以的,除了一些更深奥的部分,但是 C++17 草稿中的功能还不可用。
有 2 个成员的 class/struct?
指向 int
和 bool
的指针(布尔值表示指针指向一个或三个值)?
#include <iostream>
struct foo
{
bool single;
int const * p;
constexpr foo ( int const & x ) : single{true}, p{&x}
{}
constexpr foo ( int const * y ) : single{false}, p{y}
{}
};
constexpr int x{2};
constexpr int y[]{3, 5, 7};
int main ()
{
constexpr foo f1{x};
constexpr foo f3{y};
}
似乎您可以使用的最好的东西是有条件的整数范围:
class A {
std::array<int, 3> data;
bool one;
public:
constexpr A(int x): data{{x, 0, 0}}, one(true) { }
constexpr A(int x, int y, int z): data{{x, y, z}}, one(false) { }
constexpr size_t size() const { return one ? 1 : 3; }
constexpr const int* begin() const { return data.data(); }
constexpr const int* end() const { return data.data() + size(); }
};
我不完全确定你选择哪个元素的逻辑是什么,但如果你有一个 1 尺寸的范围,你会得到那个元素,如果你有一个 3 尺寸的范围,那么你会做任何事情你需要做的。
不管具体细节如何,重点是你想要一个有3个int
和一个bool
的类型。您需要存储 3 int
,您希望它是字面值,并且您希望它知道它存储的是 3 还是 1 int
。这几乎说明了类型。剩下的唯一事情就是确定您希望数据访问的外观如何,而这并没有指定。