函数返回一个由向量组成的元组。

13

我试图避免在我的函数中使用输出参数。旧的函数是:

void getAllBlockMeanError(
    const vector<int> &vec, vector<int> &fact, vector<int> &mean, vector<int> &err)

这里vec是输入参数,factmeanerr是输出参数。我试图将输出参数分组为一个元组:

tuple< vector<int>, vector<int>, vector<int> > 
                                  getAllBlockMeanErrorTuple(const vector<int> &vec)
{
    vector<int> fact, mean, err;
    //....
    return make_tuple(fact, mean, err);
}

现在我可以使用以下代码调用新函数:

tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec);

对我来说,它看起来更清洁。但我有一个问题,tie(fact,mean,err)的等号赋值是如何工作的?它进行深度复制还是移动?由于 getAllBlockMeanErrorTuple 内部的 factmeanerr 将被销毁,我希望它是执行移动而不是深度复制。


我不确定,但我认为按照现有的写法它会复制向量。如果你将向量移入make_tuple()中,则它们应该被移动。 - Andy
5
我会使用 struct Result { vector<int> fact; vector<int> mean; vector<int> err; }; 代替元组,以获得更好的getter命名。 - Jarod42
2个回答

16
你的函数签名是tuple< vector<int>, vector<int>, vector<int> >,其中包含了可被移动的元素。
std::tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec)

应该进行移动赋值 (move-assign) fact, mean, 和 err

以下是一个样例程序,供您自行查看 (演示):

#include <iostream>
#include <vector>
#include <tuple>

struct A
{
    A() = default;
    ~A() = default;
    A(const A&)
    {
        std::cout << "Copy ctor\n";
    }
    A(A&&)
    {
        std::cout << "Move ctor\n";
    }
    A& operator=(const A&)
    {
        std::cout << "Copy assign\n";
        return *this;
    }
    A& operator=(A&&)
    {
        std::cout << "Move assign\n";
        return *this;
    }
};

std::tuple<A, A> DoTheThing()
{
    A first;
    A second;
    return std::make_tuple(first, second);
}

int main()
{
    A first;
    A second;
    std::tie(first, second) = DoTheThing();
}

输出:

拷贝构造函数
拷贝构造函数
移动赋值函数
移动赋值函数

请注意,该函数必须创建向tuple返回的向量的副本,这可能不是您想要的。您可以使用std::move将元素移动到std::make_tuple中:

return make_tuple(std::move(fact), std::move(mean), std::move(err));

这里是与上面相同的示例,但在make_tuple中使用了std::move

请注意,使用C++17的Structured Bindings,您可以完全忘记使用std::tie,并更多地依赖于auto(感谢@Yakk):

auto[fact, mean, err] = getAllBlockMeanErrorTuple(vec);

早期的C++17标准在clang(3.8.0)和gcc(6.1.0)中尚未得到支持,但是在clang 4.0.0中似乎有一些支持:演示(感谢@Revolver_Ocelot)。

您会注意到使用结构化绑定的输出更改为:

移动构造函数
移动构造函数

这表明它们利用了复制省略,从而节省了额外的移动操作。


1
auto[fact, mean, err] = getAllBlockMeanErrorTuple(vec); - Yakk - Adam Nevraumont
1
@Yakk:那不是C++17吗? - AndyG
是的,但我认为值得一提,至少顺便提一下。 - Yakk - Adam Nevraumont
1
clang 4 对于结构化绑定提供了有限的支持:http://melpon.org/wandbox/permlink/SAns8Kzsz9FgSmzt - Revolver_Ocelot
@Revolver_Ocelot:非常感谢。帖子已更新。 - AndyG
非常感谢,这真的很有帮助。 - Hao Shi

12
std::tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec);

我会进行移动赋值操作。

但如评论中所提到的

return make_tuple(fact, mean, err);

如果你想复制,可以用以下方法解决:

return make_tuple(std::move(fact), std::move(mean), std::move(err));

至少提到C++17结构化绑定!我们距离它的到来只有几个月了!(对于“这里”的有限值) - Yakk - Adam Nevraumont
谢谢Jarod42。 - Hao Shi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接