C++11中返回元组的最佳方法是什么?

27

我想从一个函数中返回一些值,并将它们打包在元组中。因此,我有两种函数声明的可能性:

std::tuple<bool, string, int> f()
{
  ...
  return std::make_tuple(false, "home", 0);
}

std::tuple<bool, string, int> f()
{
  ...
  return std::forward_as_tuple(false, "home", 0);
}

这些函数是等价的吗? 你更喜欢哪一个函数?


3
你是否在转发那些东西?没有?使用std::make_tuple - Xeo
好的,这也不错。但是make_tuple和forward_as_tuple之间的真正区别是什么? - Gian Lorenzo Meocci
1
@GianLorenzoMeocci:forward_as_tuple 用于在需要转发万能引用但作为元组时使用。如果您不知道这是什么意思,请不要使用它。 - Mooing Duck
2个回答

22

std::forward_as_tuple() 创建一个由 引用 组成的元组。因为您无论如何都返回一个 tuple<bool, string, int>,所以在这种情况下两者等效,但我认为第一种方法更清晰明了——当您没有转发任何内容时使用 forward_as_tuple() 是令人困惑的。

此外,正如Sebastian Redl在评论中提到的,make_tuple() 可以让编译器执行复制省略——根据C++11标准第12.8/31段,而 forward_tuple() 则不能(因为它返回的内容与函数的返回类型不同)。


4
更高效。第一种方法可能会使用复制省略(如果您将字符串字面值包装在std::string构造函数调用中),而第二种方法则不能,因为它不是普通的复制。 - Sebastian Redl
@SebastianRedl:没错,值得一提。谢谢。 - Andy Prowl

7

I prefer,

std::tuple<bool, std::string, int> f()
{
  ...
  return { false, "home", 0 };
}

编辑1

上面的代码在clang/libc++ trunk下实际上可以编译通过。正如@AndyProwl在评论中所说,这不应该发生,因为std::tuple构造函数是显式的,并且通过初始化列表语法返回处于复制初始化上下文中,因此是复制列表初始化,当匹配到显式构造函数时失败。

我不知道clang/libc++之所以能够通过,我认为这可能是libc++中的一个bug。无论如何,不能对元组使用这种语法令人伤心...

我认为我意识到了这一点有多伤心(至少对我来说)。我开始习惯了这种语法,但人们被迫事先知道返回类型是否包含显式构造函数,才能使其工作。

编辑2

这确实是libc++的扩展,更多信息请查看Howard Hinnant在这里的回答:https://dev59.com/dWUp5IYBdhLWcg3w5KvV#14963014

这也是目前在libc++ bug列表中开放的:http://llvm.org/bugs/show_bug.cgi?id=15299

这是相关提案:Daniel Krügler,Improving pair and tuple

简而言之,这就是libc++中发生的情况:

#include <tuple>
#include <string>

struct S
{
    explicit S(int) {}
};

int main()
{
    std::tuple<int, std::string> t1 = { 1, "hello" }; // ok
    std::tuple<std::string> t2      = "hello";        // ok
    std::tuple<int, S> t3           = { 1, 1 };       // fail: an *element* is to be constructed explicitly
}

4
我也希望如此,但是 std::tuple 有一个显式构造函数,这意味着上面的代码将无法编译。 - Andy Prowl
4
希望有人能将N3452提交到未来的标准中。 - Morwenn
1
@Morwenn:不知道这个,谢谢。但实际上,将“tuple”的构造函数设置为非显式是否足够?我不明白为什么必须这样做。毕竟,“pair”的构造函数并不是显式的。 - Andy Prowl
如果允许显式构造函数进行复制列表初始化,会有什么问题? - R. Martinho Fernandes
1
@Andy 不太好。这意味着如果我想要可移植的代码,我必须使用clang+libc++作为我的主编译器,这可能会让人感到烦恼 :( 我希望编译器/库的编写者能够合理地处理所有这些扩展,并使其成为可选项。现在,我需要传递标志才能将编译器设置为“真正的C++”模式,这已经很糟糕了。更糟糕的是,即使这样做也不能给我想要的结果。 - R. Martinho Fernandes
显示剩余14条评论

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