在一个采用相同类型的多个参数的函数中,我们如何保证调用者不会搞乱排序?
例如
void allocate_things(int num_buffers, int pages_per_buffer, int default_value ...
Run Code Online (Sandbox Code Playgroud)
然后
// uhmm.. lets see which was which uhh..
allocate_things(40,22,80,...
Run Code Online (Sandbox Code Playgroud)
Die*_*Epp 69
典型的解决方案是将参数放在具有命名字段的结构中.
AllocateParams p;
p.num_buffers = 1;
p.pages_per_buffer = 10;
p.default_value = 93;
allocate_things(p);
Run Code Online (Sandbox Code Playgroud)
当然,您不必使用字段.您可以使用成员函数或任何您喜欢的函数.
Nir*_*man 30
到目前为止,有两个好的答案,还有一个:另一种方法是尝试尽可能利用类型系统,并创建强大的typedef.例如,使用boost strong typedef(http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/strong_typedef.html).
BOOST_STRONG_TYPEDEF(int , num_buffers);
BOOST_STRONG_TYPEDEF(int , num_pages);
void func(num_buffers b, num_pages p);
Run Code Online (Sandbox Code Playgroud)
使用错误顺序的参数调用func现在将是编译错误.
关于此的几点说明.首先,boost的强大typedef在其方法上相当过时; 你可以使用可变参数CRTP做更好的事情并完全避免使用宏.其次,显然这会引入一些开销,因为您经常需要显式转换.所以一般来说你不想过度使用它.对于在您的图书馆中反复出现的事情,这真的很不错.对于那些作为一次性出现的东西来说不太好.因此,举例来说,如果你正在编写一个GPS库,那么你应该有一个强大的双模式,以米为单位的距离,一个强大的int64 typedef,用于超过纪元的时间,以纳秒为单位,依此类推.
ser*_*gej 30
如果您有C++ 11编译器,则可以将用户定义的文字与用户定义的类型结合使用.这是一个天真的方法:
struct num_buffers_t {
constexpr num_buffers_t(int n) : n(n) {} // constexpr constructor requires C++14
int n;
};
struct pages_per_buffer_t {
constexpr pages_per_buffer_t(int n) : n(n) {}
int n;
};
constexpr num_buffers_t operator"" _buffers(unsigned long long int n) {
return num_buffers_t(n);
}
constexpr pages_per_buffer_t operator"" _pages_per_buffer(unsigned long long int n) {
return pages_per_buffer_t(n);
}
void allocate_things(num_buffers_t num_buffers, pages_per_buffer_t pages_per_buffer) {
// do stuff...
}
template <typename S, typename T>
void allocate_things(S, T) = delete; // forbid calling with other types, eg. integer literals
int main() {
// now we see which is which ...
allocate_things(40_buffers, 22_pages_per_buffer);
// the following does not compile (see the 'deleted' function):
// allocate_things(40, 22);
// allocate_things(40, 22_pages_per_buffer);
// allocate_things(22_pages_per_buffer, 40_buffers);
}
Run Code Online (Sandbox Code Playgroud)
(注意:帖子最初被标记为'C`)
C99以后允许扩展到@Dietrich Epp的想法:复合文字
struct things {
int num_buffers;
int pages_per_buffer;
int default_value
};
allocate_things(struct things);
// Use a compound literal
allocate_things((struct things){.default_value=80, .num_buffers=40, .pages_per_buffer=22});
Run Code Online (Sandbox Code Playgroud)
甚至可以传递结构的地址.
allocate_things(struct things *);
// Use a compound literal
allocate_things(&((struct things){.default_value=80,.num_buffers=40,.pages_per_buffer=22}));
Run Code Online (Sandbox Code Playgroud)
你不能.这就是为什么建议尽可能少的函数参数.
在您的例子中,你可以像独立的功能set_num_buffers(int num_buffers),set_pages_per_buffer(int pages_per_buffer)等等.
您可能已经注意到自己allocate_things并不是一个好名字,因为它没有表达该功能实际上在做什么.特别是我不希望它设置默认值.
为了完整起见,您可以在通话结束时使用命名参数.
void allocate_things(num_buffers=20, pages_per_buffer=40, default_value=20);
// or equivalently
void allocate_things(pages_per_buffer=40, default_value=20, num_buffers=20);
Run Code Online (Sandbox Code Playgroud)
但是,使用当前的C++,这需要相当多的代码来实现(在头文件声明中allocate_things(),它还必须声明适当的外部对象num_buffers等,提供operator=返回唯一合适的对象).
----------工作实例(对于sergej)
#include <iostream>
struct a_t { int x=0; a_t(int i): x(i){} };
struct b_t { int x=0; b_t(int i): x(i){} };
struct c_t { int x=0; c_t(int i): x(i){} };
// implement using all possible permutations of the arguments.
// for many more argumentes better use a varidadic template.
void func(a_t a, b_t b, c_t c)
{ std::cout<<"a="<<a.x<<" b="<<b.x<<" c="<<c.x<<std::endl; }
inline void func(b_t b, c_t c, a_t a) { func(a,b,c); }
inline void func(c_t c, a_t a, b_t b) { func(a,b,c); }
inline void func(a_t a, c_t c, b_t b) { func(a,b,c); }
inline void func(c_t c, b_t b, a_t a) { func(a,b,c); }
inline void func(b_t b, a_t a, c_t c) { func(a,b,c); }
struct make_a { a_t operator=(int i) { return {i}; } } a;
struct make_b { b_t operator=(int i) { return {i}; } } b;
struct make_c { c_t operator=(int i) { return {i}; } } c;
int main()
{
func(b=2, c=10, a=42);
}
Run Code Online (Sandbox Code Playgroud)
你真的要尝试QA任意整数的所有组合吗?抛出所有负值/零值等检查?
只需为最小,中等和最大缓冲区数量以及中小型缓冲区大小创建两个枚举类型.然后让编译器完成工作,让你的QA人员休息一下:
allocate_things(MINIMUM_BUFFER_CONFIGURATION, LARGE_BUFFER_SIZE, 42);
Run Code Online (Sandbox Code Playgroud)
那么你只需要测试有限数量的组合,你将获得100%的覆盖率.5年后从事代码工作的人只需知道他们想要实现的目标,而不必猜测他们可能需要的数字或实际在现场测试的数值.
它确实使代码稍微难以扩展,但听起来这些参数适用于低级性能调优,因此不应将这些值视为廉价/微不足道/不需要彻底测试.来自allocate_something(25,25,25)的更改的代码审查;
...至
allocate_something(30,80,42);
......可能只会耸耸肩,但是对新的枚举值EXTRA_LARGE_BUFFERS的代码审查可能会引发关于内存使用,文档,性能测试等的所有正确讨论.