使用统一初始化语法从函数返回元组

时间:2013-02-19 16:00:38

标签: c++ gcc c++11 tuples

以下代码使用clang(libc ++)编译,并使用gcc(libstdc ++)失败。为什么gcc(libstdc ++)抱怨初始化列表?我认为return参数使用统一初始化语法。

std::tuple<double,double> dummy() {
  return {2.0, 3.0};
}

int main() {   
  std::tuple<double,double> a = dummy();   
  return 0;
}
  

错误:第22行:从初始化程序转换为'std :: tuple'   list将使用显式构造函数'constexpr std :: tuple&lt; _T1,_T2&gt; :: tuple(_U1&amp; \   &amp;,_U2&amp;&amp;)[与_U1 = double; _U2 =双倍; =无效_T \   1 =双倍; _T2 = double]'

注意: GCC(libstdc ++)(和clang(libc ++))接受

std::tuple<double,double> dummy {1.0, 2.0};

是不是一样的情况?

更新:这是一个libc ++扩展程序,请参阅http://llvm.org/bugs/show_bug.cgi?id=15299,并在下面由Howard Hinnant回答。

2 个答案:

答案 0 :(得分:33)

pair<>不同,遗憾的是,tuple<>的隐式构造是不可能的。您必须使用make_tuple()

#include <tuple>

std::tuple<double,double> dummy()
{
    return std::make_tuple(2.0, 3.0); // OK
}

int main() 
{   
    std::tuple<double,double> a = dummy();   
    return 0;
}

std::tuple有一个可变参数构造函数,但标记为explicit。因此,它不能用于这种情况,其中临时必须是可隐式构造的。根据C ++ 11标准的第20.4.2段:

namespace std {
    template <class... Types>
    class tuple {
    public:

        [...]
        explicit tuple(const Types&...); // Marked as explicit!

        template <class... UTypes>
        explicit tuple(UTypes&&...);     // Marked as explicit!

出于同样的原因,使用复制初始化语法来初始化元组是非法的:

std::tuple<double, double> a = {1.0, 2.0}; // ERROR!
std::tuple<double, double> a{1.0, 2.0}; // OK

或者在将它作为参数传递给函数时隐式构造元组:

void f(std::tuple<double, double> t) { ... }
...
f({1.0, 2.0}); // ERROR!
f(make_tuple(1.0, 2.0)); // OK

因此,如果在std::tuple中返回dummy()时明确构造#include <tuple> std::tuple<double,double> dummy() { return std::tuple<double, double>{2.0, 3.0}; // OK } int main() { std::tuple<double,double> a = dummy(); return 0; } ,则不会出现编译错误:

{{1}}

答案 1 :(得分:23)

Andy Prowl给出的答案是正确的。我想对libc ++实现发表评论,评论格式不允许我有足够的空间或格式选择。

DanielKrügler和我在一年前就这个问题进行了一次对话,他让我相信这个问题值得对libc ++进行扩展以获得实地经验。到目前为止,反馈都是积极的。不过我想澄清一点:这并不像从explicit删除explicit constexpr tuple(UTypes&&...)那么简单。

Daniel的计划是给tuple一个完全尊重每个元素的隐式/显式构造的构造函数。并且 if 每个元素将从初始化列表中的每个参数隐式构造,然后元组构造是隐式的,否则它将是显式的。

例如:

假设:

#include <tuple>

struct A
{
};

struct B
{
    B() = default;
    B(A);
};

struct C
{
    C() = default;
    explicit C(A);
};

然后:

std::tuple<>
test0()
{
    return {};  // ok
}

没有什么可说的。但这也没关系:

std::tuple<B>
test1B()
{
    return {A()};  // ok B(A) implicit
}

因为从AB的转换是隐含的。但是以下是编译时错误:

std::tuple<C>
test1C()
{
    return {A()};  // error, C(A) is explicit
}

因为从AC的转换是明确的。这种逻辑继续用于多元素元组。要进行隐式转换,每个元素必须从参数列表中进行隐式转换:

std::tuple<A, B>
test2B()
{
    return {A(), A()};  // ok each element has implicit ctor
}

std::tuple<A, C>
test2C()
{
    return {A(), A()};  // error, C(A) is explicit
}

我应该强调:目前这是一个libc ++扩展。

<强>更新

chico提出了一个很好的建议,我更新了这个答案:

自从给出了答案以来,DanielKrügler写了一篇paper并于今年四月将其提交给布里斯托尔的C ++委员会。虽然该文件很受欢迎,但在本周进行了审查,将其投入当前的工作草案。

<强>更新

丹尼尔的提议现在是current working draft的一部分。现在,libc ++实现已成为下一个C ++标准(我们希望的C ++ 17)的标准。