系统地缩放C ++函数的返回结构

时间:2018-09-11 07:27:06

标签: c++ function class c++17

我有几个函数,它们不仅返回一个变量,而且返回一组变量。例如,寻根器可能会返回找到的根,所需的迭代以及寻根过程是否确实成功。

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 };
}

现在,如果我仅返回少量变量,则此方法有效。但是也许我想在我的const std::string errorMessage中包括一个可能的错误消息,例如ReturnData,以及其他参考元数据。因此,当将其缩放到10个以上的参数时,在使用初始化列表返回数据时很容易出错。也许我也删除了一个参数,或者重新排列了它们的顺序,依此类推,这又需要我在函数findRoot的末尾仔细地对初始化列表进行相同的更改。

从这样的函数返回数据的好的系统方法是什么?我的标准是:

  1. 清晰易懂的代码,易于他人理解/维护。
  2. 多少高效的代码,这意味着我不想构造过于复杂的返回结构(因为该根查找器可能会多次调用,并且在大多数情况下,人们只会在乎关于变量root)。

4 个答案:

答案 0 :(得分:3)

您可能应该只创建一个变量并设置其成员:

    //...

    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;
};

答案 1 :(得分:1)

当您担心返回值未正确初始化时,请添加一个构造函数,以确保将每个必需的参数传递给它。由于构造函数现在可以处理类不变式,因此数据成员不再应该是公共的,并且需要getter方法。如果这听起来过于复杂,则您需要承担可能无法正确构造返回值的风险。或者-保持数据成员公开并使其const。您失去了分配给该类型实例的能力,但是如果仅在调用findRoot时对其进行初始化,则可以接受。

在扩展性方面,任何解决方案都会被接受,因此只花了我两分钱:不要犹豫添加更多struct,并将它们作为数据成员包含在ReturnData中。可以以合理方式进行分组的任何参数组都可以证明一种新类型。每当函数返回的信息应该是可选的时,请使用std::optional。这正是类型所在。

最后,正如您在问题中提到的效率一样,当您认为存在瓶颈时,请首先进行测量。 ReturnData不太可能会使应用程序变慢。不仅因为返回值优化,而且因为findRoot必须非常复杂才能收集大量数据以返回,因此函数本身可能会主导任何性能分析。

答案 2 :(得分:0)

如果您要定位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 }
}; 

答案 3 :(得分:0)

一种方法是赋予值唯一的类型。
有点乏味,但是有效。
您可以使用模板来减少繁琐的工作。

遵循以下原则:

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 { Root{root}, Success{true} }
}