系统地缩放 C++ 函数的返回结构 [关闭]
Posted
技术标签:
【中文标题】系统地缩放 C++ 函数的返回结构 [关闭]【英文标题】:Scaling Return Structures of C++ Functions Systematically [closed] 【发布时间】:2018-09-11 07:27:05 【问题描述】:我有几个函数不仅返回一个变量,而且返回一组变量。例如,根查找器可能会返回找到的根、所需的迭代以及根查找过程是否确实成功。
struct ReturnData
const double root;
const size_t iterations;
const bool success;
;
template <class FuncType>
ReturnData findRoot(const FuncType& f, const double& guess)
double root = guess;
size_t iterations = 0;
while(true) /*... find root ...*/
return root, iterations, true ;
现在,如果我只返回少量变量,这将有效。但也许我想在我的ReturnData
中包含一个可能的错误消息,如const std::string errorMessage
,以及其他参考元数据。因此,当将其扩展到 10 个以上的参数时,在使用初始化列表返回数据时开始很容易出错。也许我还删除了一个参数,或者我对它们重新排序等等,这反过来要求我在函数 findRoot
的末尾仔细地将相同的更改应用于初始化列表。
从这样的函数中返回数据的良好系统方法是什么?我的标准是:
-
清晰的代码易于其他人理解/维护。
有点高效的代码,这意味着我不想构建过于复杂的返回结构(因为这个根查找器可能会被多次调用,并且在大多数情况下人们只会关心变量 @ 987654326@)。
【问题讨论】:
这可能应该继续Software Engineering。 "所以当缩放到超过 10 个参数时" 如果你有 10 个 独立 值要返回,那么你的函数正在执行太多了。现在,您有 3 个值:感兴趣的实际值、如果生成值时重要的一些补充数据,以及如果操作无法完成时的可选错误(理论上应该是一个异常,或者在至少expected
)。除此之外,你应该认真质疑这个函数做了多少事情,以及调用者接收这些值的重要性。
【参考方案1】:
您可能应该只创建一个变量并设置其成员:
//...
ReturnData rd;
rd.root = root;
rd.iterations = iterations;
rd.success = true;
return rd;
;
因为有 RVO,所以我不会担心性能。
如果您过于关心性能,您可以在函数开头声明一个变量 ReturnData
并使用它的成员:
template <typename FuncType>
ReturnData findRoot(FuncType const &f, double const &guess)
ReturnData rd;
while( /* ... */ ) /*...*/ rt.root = /*...*/; ++rd.iterations;
rd.success = true;
return rd;
;
【讨论】:
提供的编译器正确支持 NRVO... 问题被标记为 C++17,因此保证复制省略无论如何都会发挥作用。 这是有道理的,但我必须将ReturnData
中的变量设为非const,我不想这样做。
@lubgr 我只是在暗示要考虑实施的质量。我知道这里有两个大玩家,一个编译器倾向于不做 RVO,而另一个则避免 NRVO。
@Phil-ZXX 可以使用原型概念吗?【参考方案2】:
当您担心返回值未正确初始化时,请添加一个构造函数,以确保将每个必要的参数都传递给它。由于构造函数现在处理类不变量,因此数据成员不应再是公共的,您需要 getter 方法。如果这听起来过于复杂,那么您需要承担返回值可能被错误构建的风险。或者 - 将数据成员公开并设为const
。您失去了分配给这种类型实例的能力,但如果您只在调用 findRoot
时初始化它们,这可能是可以接受的。
在扩展方面,任何解决方案都将是固执己见,因此只有我的两分钱:不要犹豫,进一步添加 struct
s 并将它们作为数据成员包含在 ReturnData
中。任何可以以合理方式分组的参数组都证明了新类型的合理性。只要函数返回的信息应该是可选的,那么就使用std::optional
。这正是类型的意义所在。
最后,正如您在问题中提到的效率 - 当您认为存在瓶颈时,首先进行测量。 ReturnData
不太可能减慢应用程序的速度。不仅因为返回值优化,还因为 findRoot
必须非常复杂才能收集如此多的数据以返回,以至于函数本身可能会主导任何性能分析。
【讨论】:
【参考方案3】:如果你的目标是 gcc 和/或 clang,你可以使用指定的初始化器:
return
.root = root,
.iterations = iterations,
.success = true
;
这最初是在 C99 中引入的,并计划用于 C++2a。 gcc 和 clang 都支持它作为扩展,不幸的是 MSVC 不支持。
替代方法可以使用结构,如下所示:
struct Root double value; ;
struct Iterations size_t value; ;
struct Success bool value; ;
struct ReturnData
const Root root;
const Iterations iterations;
const Success success;
;
// usage:
return
Root root ,
Iterations iterations ,
Success true
;
【讨论】:
在你的例子中获取值,你需要做ReturnData.root.value,对吗?你不是有点超越结构的想法吗?【参考方案4】:一种方法是赋予值唯一的类型。 这有点乏味,但它有效。 您可以使用模板来减少繁琐。
类似的东西:
template<typename T, typename>
struct NewType
T value;
;
using Root = NewType<double, struct Root_T>;
using Success = NewType<bool, struct Success_T>;
struct ReturnData
const Root root;
const Success success;
;
template <class FuncType>
ReturnData findRoot(const FuncType& f, const double& guess)
double root = guess;
while(true) /*... find root ...*/
return Rootroot, Successtrue
【讨论】:
以上是关于系统地缩放 C++ 函数的返回结构 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章