系统地缩放 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 时初始化它们,这可能是可以接受的。

在扩展方面,任何解决方案都将是固执己见,因此只有我的两分钱:不要犹豫,进一步添加 structs 并将它们作为数据成员包含在 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++ 函数的返回结构 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C++ 引用与返回值

C++的一个函数的返回值可以是结构体吗?

字符串函数不返回字符串? c++ [关闭]

如何从 C++ 中的函数返回结构?

在 SWIG 中将结构从 C++ 函数返回到 Python

如何将结构传递给 c++ 函数并通过一些修改返回相同的结构?