一个函数有两个版本(下面的代码是简化版)。这两个版本都在程序中使用。在实际功能中,两个版本之间的差异可能发生在两个或三个不同的地方。
如何在不牺牲性能的情况下,通过模板或其他方式避免在代码中编写两个版本?这是一次尝试 使代码更具可读性。
性能至关重要,因为它会运行很多次,我正在为不同的实现编写基准。
(另外,如果我为少数人写图书馆,这是一个好的api吗?)
示例:
int set_intersect(const int* A, const int s_a,
const int* B, const int s_b,
int* C = 0){
//if (int* C == 0), we are running version
//0 of the function.
//int* C is not known during compilation
//time for version 1.
int Count0 = 0;
//counter for version 0 of the function.
const int* const C_original(C);
//counter and pointer for version 1 of
//the function
int a = 0;
int b = 0;
int A_now;
int B_now;
while(a < s_a && b < s_b){
A_now = A[a];
B_now = B[b];
a += (A_now <= B_now);
b += (B_now <= A_now);
if (A_now == B_now){
if (C == 0){
Count0++;
} else {
C++;
*(C)=A_now;
}
}
}
if (C == 0){
return Count0;
}else{
return C - C_original;
}
}
感谢。
更新
Conditional compile-time inclusion/exclusion of code based on template argument(s)
(其中一些模板看起来很长)
Remove/Insert code at compile time without duplication in C++
(这与我的情况更相似。但我的情况更简单。)
我想以下内容可行,但它增加了一个新论点。
int set_intersect(const int* A, const int s_a,
const int* B, const int s_b,
int* C = 0,
char flag);
将版本0的所有代码放入if (flag == '0') { /* version 0 code */ }
将版本1的所有代码放入if (flag == '1') { /* version 1 code */}
可能可以将标志变量放入模板中(正如Barmar在评论中建议的那样),这样,它不会为函数添加另一个参数。也可以用枚举替换0和1(如enum class set_intersection_type {find_set, size_only}
)。调用该函数将类似于set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b, int* C)
或set_intersect<size_only>(const int* A, const int s_a, const int* B, const int s_b)
希望这比以前更具可读性,编译器足够聪明,可以看到发生了什么。
另一个问题是,如果有人使用findset版本(版本1),然后忘记更改默认参数(int C * = 0),该怎么办?可以通过这种方式调用函数:set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b)
。
可能我可以在评论中使用dasblinkenlight的想法。创建两个包装函数(set_intersection
,set_intersection_size
)。每个包装器使用不同的参数调用实际函数。还将实际函数列为私有函数,因此没有人可以直接调用它。
对于集合交集的不同实现,也许可以创建带有模板的公共包装器。调用包装器类似于set_intersection<basic>
,set_intersection<binary_search>
或set_intersection_size<simd>
等。这似乎看起来更好。
答案 0 :(得分:1)
一般来说似乎可行,问题是你是否想这样做。会说不。从我可以告诉你做两件事:
我不仅要考虑速度,还要明确两个不同的功能set_intersection
和set_intersection_size
,但如果你坚持使用一个,我会根据std::set_intersection对你的代码进行基准测试,如果可能的话,只要C != 0
重定向到:: std版本。
在您当前的版本中,我不会使用您的库。但是我也很难想出一个我更喜欢STL版本的set_intersection
定制版本的情况。如果我需要比STL更好的性能,我希望已经将代码中的点确定为瓶颈,我根本不会使用库调用,而是自己编写代码,可能在汇编和展开循环等。
我有点不高兴这是怎么回事:
const int* const Count1(C);
//counter and pointer for version 1 of
//the function
...
Count1++;
*(Count1)=A_now;
答案 1 :(得分:1)
如果在编译时知道你想要什么版本,你可以使用条件编译。
#define Version_0 //assuming you know this compilation is version 0
然后你可以去:
int set_intersect(...)
#ifdef Version_0
//Version 0 of the code
#else
//Version 1 of the code
这样只会编译一个版本的代码。
如果您不知道编辑的版本,我建议您使用两个单独的功能,这样您就不必检查每个函数实例的版本。
答案 2 :(得分:1)
有一个专门研究bool参数的类型:
template<bool b>
struct Counter
{
};
template<>
struct Counter<false>
{
int c;
Counter(int *)
: c(0)
{
}
int operator++() { return ++c; }
void storeA(const int a_now) {}
};
template<>
struct Counter<true>
{
const int* const c;
Counter(int * c_orig)
: c(c_orig)
{
}
int operator++() { return ++C; }
void storeA(const int a_now) { *C = a_now; }
}
然后将Counter上的算法专门化为模板参数。请注意,对于这两种情况,这将完全相同,也就是说,您不需要专门化:
template<typename Counter>
struct SetIntersectHelper
{
static int set_intersect(const int* A, const int s_a,
const int* B, const int s_b,
int* C)
{
// your function's body, using Counter
}
};
现在,您已准备好添加通用方法:
int set_intersect(const int* A, const int s_a,
const int* B, const int s_b,
int* C = 0)
{
return C ? SetIntersectHelper< Counter< true > >::set_intersect(A, s_a, B, s_b, C):
SetIntersectHelper< Counter< false > >::set_intersect(A, s_a, B, s_b, C);
}