我设计了一个参数类,允许我编写如下代码:
//define parameter
typedef basic_config_param<std::string> name;
void test(config_param param) {
if(param.has<name>()) { //by name
cout << "Your name is: " << param.get<name>() << endl;
}
unsigned long & n = param<ref<unsigned long> >(); //by type
if(param.get<value<bool> >(true)) { //return true if not found
++n;
}
}
unsigned long num = 0;
test(( name("Special :-)"), ref<unsigned long>(num) )); //easy to add a number parameter
cout << "Number is: " << num; //prints 1
该类的性能非常快:一切都只是堆栈的引用。为了保存所有信息,我在使用最多5个参数的内部缓冲区进行堆分配之前减少每个对象的大小,但这很容易改变。
为什么不经常使用这种语法,重载operator,()
来实现命名参数?是因为潜在的性能损失?
另一种方法是使用命名的习语:
object.name("my name").ref(num); //every object method returns a reference to itself, allow object chaining.
但是,对我来说,重载operator,()
看起来更“现代”的C ++,只要你不忘记使用双括号。即使它比正常功能慢,性能也不会受到太大影响,因此在大多数情况下它可以忽略不计。
我可能不是第一个想出这样的解决方案的人,但为什么它不常见?在我写一个接受它的类之前,我从未见过像上面的语法(我的例子),但对我来说看起来很完美。
答案 0 :(得分:58)
我的问题是为什么不再使用这种语法,重载operator,()来实现命名参数。
因为它是反直觉的,非人类可读的,可以说是一种糟糕的编程习惯。除非您想破坏代码库,否则请避免这样做。
test(( name("Special :-)"), ref<unsigned long>(num) ));
我们说第一次看到这段代码片段。我的思维过程是这样的:
()
内的情况。Test
是一个类,但test
是一个函数),并且您没有C
个前缀。)name
后,我发现它是类。并且它会使,
运算符超载,因此它实际上不再丢弃第一个参数。 看看这里浪费了多少时间?坦率地说,写这样的东西可能会让你遇到麻烦,因为你使用语言特性使你的代码看起来像你的代码实际上做的不同(用一个参数进行函数调用看起来有两个参数或那个它是一种可变函数)。这是一个糟糕的编程习惯,大致相当于重载operator +来执行替代而不是添加。
现在,让我们考虑QString示例。
QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName);
让我们说,这是我生命中的第一次。这就是我的思维过程:
status
。%1
- 样式条目并返回QString&amp ;.因此,.arg()
电话链立即有意义。请注意,QString :: arg之类的东西可以模板化,并且您可以为不同的参数类型调用它,而无需在<>
中手动指定参数类型。看起来更像&#34;现代&#34; C ++
&#34;新颖有光泽&#34;有时意味着&#34;马车和破坏&#34; (slackware linux建立在一个类似的想法上)。如果您的代码看起来很现代,则无关紧要。它应该是人类可读的,它应该做它想要做的事情,你应该浪费尽可能少的时间来编写它。即你应该(个人推荐)的目标是以最低的成本(包括维护)在最短的时间内实现最大量的功能,但是获得最大的奖励。遵循KISS原则也是有道理的。
你的&#34;现代&#34;语法不会降低开发成本,不会缩短开发时间,并且会增加维护成本(反直觉)。因此,应避免使用此语法。
答案 1 :(得分:3)
没有必要。您的动态调度(行为方式不同,取决于参数的逻辑类型)可以更容易实现a)b)使用模板专业化更快。
如果您确实需要基于仅在运行时可用的信息进行区分,我会尝试将您的test
函数移动为param
类型的虚拟方法,并简单地使用动态绑定(这就是它的用途,这就是你重新发明的东西)。
这种方法更有用的唯一情况可能是多调度方案,您希望减少代码并找到一些相似性模式。