在C ++ 11

时间:2018-12-21 08:38:22

标签: c c++11 refactoring legacy-code perfect-forwarding

我必须使用几个传统的C函数,这些函数通常具有输入,输入输出和输出参数,并且我想将它们包装在C ++函数中。

(为了简化起见,我使用double作为参数,但我认为该问题对于包含大量成员的沉重struct的参数也是有效的。)

C函数如下所示:

void Cfun(double* in_a, double* io_b, double* out_c, double* in_destr_d){}

在文档中说,第一个参数a是输入参数,b是输入输出,c是输出,d是输入参数返回时将包含未指定的(垃圾)值。 (而且所有指针都必须为非空,并且只采用一个元素,因此不涉及任何数组。)

在C ++中,很明显,人们可以通过以下方式重构Cfun而不会造成损失:

double fun0(double a, double& b, double d){
    double c;
    Cfun(const_cast<double*>(&a), &b, &c, &d);
    return c;
}

这应该起作用,但是也将涉及原始C版本中没有副本的副本。因此,鉴于我们对Cfun(文档+我们知道我们在做什么)的了解,一个更好的版本可能是:

double fun1(double const& a, double& b, double& d){
    double c;
    Cfun(const_cast<double*>(&a), &b, &c, &d);
    return c;
}

也就是说,a提升为const引用,bd提升为引用,c提升为返回值。

此功能可以用作:

double a = 1.1;
double b = 1.2;
double d = 1.3;

i)

...
double c1 = fun1(1.1, b, d); 

ii)

...
double c1 = fun1(a  , b, d);
// b has useful information here
// d has garbage

第二个和第三个参数必须是l值,这很好。

但是,参数bd之间存在细微的差别。尽管b具有有用的信息,d实际上已被销毁,并且在函数调用之后使用其值在任何情况下都是错误的。

这种情况让我想起了r值参考,并在C ++ 11中有所发展。 移动的r值参数最终处于未指定但可分配的状态。 这使我认为C语言中的垃圾输入参数可以或多或少地映射到r值引用。

换句话说,包装器可以用这种方式编写,以传达d的值,不仅会改变,而且会保持不可用状态。

double fun2(double const& a, double& b, double&& d){
    double c;
    Cfun(const_cast<double*>(&a), &b, &c, &d);
    return c;
}

听起来合理吗,我是否通过定义此包装器而损失了一些东西?在类似的情况下会发生这种事情吗?这是否可以将r值证明为内置类型(例如此处的double)?

我发现一个小问题是,即使从函数调用后d的可用性来看,语法也会变得更加冗长。

据我所知,可能的语法更改为:

i)

...
//  double c = fun2(1.1, b, d); // now compile error, d is not a r-value
double c = fun2(1.1, b, std::move(d));  
// obvious error to use the value of d, since it was (probably) moved. However "moving a POD??"
d = 2.; // assignment is ok

ii)

...
double c = fun2(1.1, b, 1.3); // good, I can pass a literal now, nothing to misuse

iii)

...
double c = fun2(1.1, b, double{d}); // not clear, reaction "this seems a redudant cast"

iv)

...
double c = fun2(1.1, b, copy(d)); // verbose, but "d should be fine because it was copied"

其中template<class T> T copy(T&& t){return t;}

总而言之,fun2Cfun更好地包装fun1吗?

在上述所有示例中,如果double被具有29个成员变量的结构替换(并且不想复制它们)怎么办?

1 个答案:

答案 0 :(得分:2)

我的第一个反应是C接口似乎采用了指向任意(长度未知的接口)长度数组的指针,而当您创建C ++接口时,您似乎假设(知道吗?)所有参数都会真正只是标量?当然-如果确实是这样,那一切都很好。

我认为所有参数确实都是标量的概念是正确的,

double fun(double a, double& b, double d);

并完成。