我有几个函数,它们不仅返回一个变量,而且返回一组变量。例如,寻根器可能会返回找到的根,所需的迭代以及寻根过程是否确实成功。
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
的末尾仔细地对初始化列表进行相同的更改。
从这样的函数返回数据的好的系统方法是什么?我的标准是:
root
)。答案 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} }
}