如何使用模板表达成员之间的约束
How to express constraints between members using templates?
假设我有一个包含一堆成员的结构:
struct foo {
int len;
bar *stuff;
};
碰巧stuff
将指向一个长为len
的bar
s数组。我想把它编码成stuff
的类型。所以类似于:
struct foo {
int len;
DependentLength<bar, &foo::len> stuff;
};
然后,我可以实现DependentLength
,使其表现得像一个指向条形数组的指针,但当试图查看大于foo::len
的索引时,它会断言。然而,我无法实现DependentLength<&foo::len>::operator[]
,因为运算符[]只接受一个参数,即索引,并且它需要知道"foo"对象的位置,以便取消引用成员指针模板参数并进行断言检查。
然而,我碰巧知道DependentLength在这里只能作为"foo"的成员使用。我真正想做的是告诉DependentLength在哪里可以找到相对于自身的len,而不是相对于foo指针。类似于DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;
,但这不是合法的C++。有没有一个好的或失败的邪恶的语言黑客可以使这项工作?
所以像
DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;
您要求模板根据运行时传递给它们的动态属性执行计算。。。这对模板不起作用,因为它们必须使用允许编译在compile time
处创建模板参数所请求的代码的值进行实例化。因此,传递给模板的任何值都必须在编译时可解析,而不是在运行时。
您将不得不使用动态容器类型。例如,std::vector
满足您的请求,如果您超出了底层容器的界限,std::vector::at()
函数将抛出异常。不幸的是,它没有static_assert
那么方便,但同样,在这种情况下使用static_assert
是不可能的,因为您需要运行时检查边界。此外,std::vector
还包含了operator[]
的重载、迭代器、对其大小的查询等。
您可以告诉模板要用作长度的成员的偏移量。
template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET>
class DependentArray
{
public:
T& operator[](LEN_T i_offset)
{
if (i_offset < 0) throw xxx;
if (i_offset > this->size()) throw xxx;
return this->m_pArr[i_offset];
} // []
private:
LEN_T& size()
{
return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET);
} // ()
private:
T* m_pArr;
};
struct foo
{
int len;
DependentArray<bar, int, -sizeof(int)> stuff;
};
编辑2:
想了另一个解决方案。使用一个只适合foo的类来提供size字段的偏移量,并在定义了foo并可以计算偏移量后定义其方法:
#define MEMBER_OFFSET(T,M)
(reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) -
reinterpret_cast<char*>(reinterpret_cast<T*>(0x10)))
template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER>
class FooDependentArray
{
public:
T& operator[](LEN_T i_offset)
{
if (i_offset < 0) throw xxx;
if (i_offset > this->size()) throw xxx;
return this->m_pArr[i_offset];
} // []
private:
LEN_T& size()
{
const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset();
return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset);
} // ()
private:
T* m_pArr;
};
struct FooSizeOffsetSupplier
{
static ptrdiff_t getOffset();
};
struct foo
{
int len;
DependentArray<bar, int, FooSizeOffsetSupplier> stuff;
};
ptrdiff_t FooSizeOffsetSupplier::getOffset()
{
return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr);
} // ()
这样就可以在foo中添加和删除成员。
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么不允许成员函数和非成员函数之间的函数重载?
- 空指针常量 (nullptr)、空指针值和空成员指针值之间有什么区别?
- 在VS2017中,我们如何在项目成员之间共享编译设置
- 常量成员和没有setter的私有成员之间有什么区别
- 成员变量的不同"kinds"之间的区别
- 基类和派生类中的模板成员之间的重载解析
- C++中类数据成员之间的通信
- 静态和非静态递归成员之间的差异
- 确保结构成员之间没有填充,以便将结构用作数组
- C++在同一对象的成员之间传递 ifstream 变量
- 将函数定义为静态成员和自由成员之间有什么区别
- 在成员之间共享对象
- 命名空间内的 extern const 和静态 const 类成员之间的区别
- 如何使用模板表达成员之间的约束
- Qt类及其成员之间的循环关系
- c++如何实现类成员之间的切换
- c++根据结构大小或其成员之间的最大对齐要求进行对齐
- 如何/应该在Qt中创建ui表单和数据成员之间的自动链接
- C++类的私有成员和受保护成员之间的区别是什么