使用运行时参数与编译时参数在类之间共享代码
Sharing code between classes with runtime versus compile-time parameters
考虑一个模拟关联缓存的类:
template <size_t S, size_t L, size_t W>
class AssociativeCache {
size_t which_set(size_t index) { return index % (L * W); }
// ...
};
这里,键缓存参数S
、L
和W
是编译时非类型模板参数。
还可以实现类似的类,如下所示:
class AssociativeCacheDynamic {
size_t S, L, L;
size_t which_set(size_t index) { return index % (L * W); }
// ...
};
在这里,关键参数与其他情况相同,但存储为类的成员变量。
除了在动态情况下设置缓存参数值的构造函数外,两个类的每个方法的实现也基本相同,即源代码是逐字节相同的1。
如果我想要这两个类,我怎样才能以最少的代码重复和尽可能少的肮脏宏或多个包含黑客来实现它们?
如果解决方案允许您有一些差异,则加分:例如,模板方法可能使用std::array
存储,而动态方法可能使用std::vector
。
1尽管编译后的代码通常会完全不同(因为代码不是专门用于动态情况下的固定值)。
诀窍是提出基类来封装两种形式之间的差异,同时允许派生模板类保存通用功能。 像这样:
#include <vector>
template <size_t Sp, size_t Wp, size_t Lp>
class AssociativeCacheTemplate {
protected:
static constexpr size_t S = Sp;
static constexpr size_t W = Wp;
static constexpr size_t L = Lp;
AssociativeCacheTemplate() { }
};
class AssociativeCacheDynamic {
protected:
size_t S;
size_t W;
size_t L;
public:
AssociativeCacheDynamic(size_t Sp, size_t Wp, size_t Lp): S(Sp), W(Wp), L(Lp) { }
};
template <class T>
class AssociativeCache: T {
using T::L;
using T::W;
using T::S;
public:
using T::T;
size_t which_set(size_t index) const { return index % (L * W); }
};
int test() {
AssociativeCache<AssociativeCacheTemplate<2, 16, 32>> t;
AssociativeCache<AssociativeCacheDynamic> d(2, 16, 32);
return t.which_set(3) * d.which_set(2);
}
模板基中的static constexpr
允许将模板值用作代码中的常量,而不会占用任何内存。 只要大小变量S
、W
和L
在两个基类之间相同,主AssociativeCache
类中的using
语句将能够访问两个基类值。
AssociativeCacheDynamic
构造函数的public
是使其编译所必需的。
一种方法可能如下所示:
size_t which_set_impl(size_t index, size_t line_length, size_t ways_per_set)
{
return index % (line_length * ways_per_set);
}
template <size_t S, size_t L, size_t W>
class AssociativeCache
{
public:
size_t which_set(size_t index) { return which_set_impl(index, L, W); }
};
class AssociativeCache2
{
size_t size, line_length, ways_per_set;
public:
AssociativeCache2(size_t line_length_in, size_t ways_per_set_in) : line_length(line_length_in), ways_per_set(ways_per_set_in) {}
size_t which_set(size_t index) { return which_set_impl(index, line_length, ways_per_set); }
};
int main(int argc, char** argv)
{
AssociativeCache<10, 10, 10> MyCache1 = {};
AssociativeCache2 MyCache2(10, 10);
std::cout << "1: " << MyCache1.which_set(10) << " and 2: " << MyCache2.which_set(10) << std::endl;
return 0;
}
或者,您可以让两个关联缓存从公开成员的基类扩展,例如:
class BaseCache
{
virtual size_t GetLineLength() = 0;
virtual size_t GetWaysPerSet() = 0;
size_t which_set(size_t index) { return index % (this->GetLineLength() * this->GetWaysPerSet());
}
class AssociativeCacheFoo : BaseCache
{
...
size_t GetLineLength() const final override { return line_length; }
}
template <size_t S, size_t L, size_t W>
class AssociativeCacheBar : BaseCache
{
...
size_t GetLineLength() const final override { return L; }
}
上面的例子是伪代码,未经测试,不保证编译。
并通过将这些访问器公开或which_set对它们都作为友元函数来向帮助程序函数公开实现。
相关文章:
- C++模板参数之间的比较似乎被忽略了
- RVALUE参考与const lVALUE参考作为参数之间的混淆
- Rcpp 中值和引用参数之间的差异
- 是否可以找到两个模板参数之间表达式的类型
- C++和C#中指针和函数参数之间的等价性
- 如何根据模板两个整数参数之间的关系对模板进行部分专用化
- 如何在C++17中强制执行模板和模板模板参数之间的约束
- GoogleTest参数化测试-可以在参数之间调用SetUp和TearDown
- 在进行正向声明时,不命名参数和命名参数之间有区别吗
- 常量和常量引用参数之间的区别是什么
- 这些参数之间有什么区别
- C/C++,在使用全局参数或传递参数之间进行选择
- printf函数参数之间的序列点;转换之间的顺序点是否重要
- 函数参数之间的差异
- 二维数组参数之间的区别是什么?
- & 和 * 作为参数之间的差异
- 在模板函数参数之间强制执行所需的关系
- 带括号和不带括号的可变参数之间的差异
- "类变量"、"类变量()"和"类变量(参数)"之间的区别
- 在C++中用 & 和 * 声明的函数参数之间的差异