利用值类型和引用的统一语法
Exploiting uniform syntax of value types and references
假设我们有以下 Base
class,以及两个派生的 classes Derived1
和 Derived2
:
class Base {
// Some declarations ...
};
class Derived1 : public Base {
// Some declarations ...
public:
vector<int> _array;
};
class Derived2 : public Base {
// Some declarations ...
public:
vector<int> &_array;
};
也就是说,我们有一个共同的class但是底层数据结构是不同类型的
假设我们想使用来自 Base
指针的统一接口访问底层数组的各个元素。即像这样的东西:
Base *bPtr = new Derived1(...);
int val = bPtr->index(3);
在 Base
中有一个名为 index
的纯虚函数是一种解决方案。然而,在那种情况下,我们需要在两个派生的 class 中使用相同的 int index(uint x) { return _array[x]; }
实现。这很丑陋,我们无法在 Base
中实现该功能,因为它不知道 _array
.
所以我们必须 (1) 将 _array
的类型更改为相同的类型(例如 vector<int>*
)并在 [= 中实现 index
13=] 或 (2) 在 Derived1
和 Derived
中有两个实现,以便使用公共接口。但是我负担不起 (1),因为它会向 Derived1
添加一个额外的间接层,我想尽可能避免这种情况。拥有许多这样的成员变量会使 (2) 变得可怕,因为这是大量的样板和复制,并且难以维护。
我理解这两个 index
函数实际上是不同的函数,因为它们对不同的类型进行操作。在许多其他场景中,Derived1
和 Derived2
需要单独的函数,因为它们具有不同的行为(并且会被编译为不同的 object 代码)。但是在 C++ 的情况下,实现在语法上是相似的,所以我想知道是否有办法利用它。
一种可能的解决方案是使用免费功能:
template <class T>
int index(const T &obj, uint x) {
return obj._array[x];
}
我们声明 index
是 Derived1
和 Derived2
的朋友。然而,这可能不被认为是优雅的,因为它使用了友元声明(同时声明两次友元是重复的)。
因此,我的问题是:
有没有更优雅的方式来实现 index
,同时避免性能成本和代码重复?
附录:
CRTP非常接近我想要的,但是它在删除现有类型Base
的同时引入了两个新类型(Base<Derived1>
是第一个,Base<Derived2>
是第二个)。这样的重构会触发代码库中的更多更改,这是不可取的(例如制作 functions/classes 使用 Base
模板 functions/classes,甚至将它们从 headers 移动到源文件)。我想要的理想情况下不需要更改代码库的其余部分。
使用CRTP.
template <typename D, typename B = Base>
class CrtpBase : public B
{
public:
int index(size_t ind){return static_cast<D*>(this)->_array[ind];}
};
struct Derived1 : public CrtpBase<Derived1>
{
vector<int> _array = {1,2,3};
};
struct Derived2 : public CrtpBase<Derived2>
{
vector<int> _arr = {4,5,6};
vector<int>& _array = _arr;
};
DEMO.
假设我们有以下 Base
class,以及两个派生的 classes Derived1
和 Derived2
:
class Base {
// Some declarations ...
};
class Derived1 : public Base {
// Some declarations ...
public:
vector<int> _array;
};
class Derived2 : public Base {
// Some declarations ...
public:
vector<int> &_array;
};
也就是说,我们有一个共同的class但是底层数据结构是不同类型的
假设我们想使用来自 Base
指针的统一接口访问底层数组的各个元素。即像这样的东西:
Base *bPtr = new Derived1(...);
int val = bPtr->index(3);
在 Base
中有一个名为 index
的纯虚函数是一种解决方案。然而,在那种情况下,我们需要在两个派生的 class 中使用相同的 int index(uint x) { return _array[x]; }
实现。这很丑陋,我们无法在 Base
中实现该功能,因为它不知道 _array
.
所以我们必须 (1) 将 _array
的类型更改为相同的类型(例如 vector<int>*
)并在 [= 中实现 index
13=] 或 (2) 在 Derived1
和 Derived
中有两个实现,以便使用公共接口。但是我负担不起 (1),因为它会向 Derived1
添加一个额外的间接层,我想尽可能避免这种情况。拥有许多这样的成员变量会使 (2) 变得可怕,因为这是大量的样板和复制,并且难以维护。
我理解这两个 index
函数实际上是不同的函数,因为它们对不同的类型进行操作。在许多其他场景中,Derived1
和 Derived2
需要单独的函数,因为它们具有不同的行为(并且会被编译为不同的 object 代码)。但是在 C++ 的情况下,实现在语法上是相似的,所以我想知道是否有办法利用它。
一种可能的解决方案是使用免费功能:
template <class T>
int index(const T &obj, uint x) {
return obj._array[x];
}
我们声明 index
是 Derived1
和 Derived2
的朋友。然而,这可能不被认为是优雅的,因为它使用了友元声明(同时声明两次友元是重复的)。
因此,我的问题是:
有没有更优雅的方式来实现 index
,同时避免性能成本和代码重复?
附录:
CRTP非常接近我想要的,但是它在删除现有类型Base
的同时引入了两个新类型(Base<Derived1>
是第一个,Base<Derived2>
是第二个)。这样的重构会触发代码库中的更多更改,这是不可取的(例如制作 functions/classes 使用 Base
模板 functions/classes,甚至将它们从 headers 移动到源文件)。我想要的理想情况下不需要更改代码库的其余部分。
使用CRTP.
template <typename D, typename B = Base>
class CrtpBase : public B
{
public:
int index(size_t ind){return static_cast<D*>(this)->_array[ind];}
};
struct Derived1 : public CrtpBase<Derived1>
{
vector<int> _array = {1,2,3};
};
struct Derived2 : public CrtpBase<Derived2>
{
vector<int> _arr = {4,5,6};
vector<int>& _array = _arr;
};
DEMO.