【问题标题】:Returning a tuple from a function using uniform initialization syntax使用统一初始化语法从函数返回元组
【发布时间】:2013-02-19 16:00:38
【问题描述】:

以下代码使用 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<_t1 _t2>::tuple(_U1&\ &, _U2&&) [with _U1 = double; _U2 = 双倍; =无效; _T\ 1 = 双倍; _T2 = 双]'

注意: 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 回答。

【问题讨论】:

  • 只是评论你的最后一个问题:不,情况不同。 return 语句是“复制初始化”上下文,而您的最后一个示例实际上是“直接初始化”。两者的区别在于复制初始化只考虑非显式构造函数。
  • Visual Studio 14 Update 3 也接受它。

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


【解决方案1】:

pair&lt;&gt; 不同,很遗憾,tuple&lt;&gt; 的隐式构造是不可能的。你必须使用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

因此,如果你在dummy()中返回时显式构造你的std::tuple,则不会出现编译错误:

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

【讨论】:

  • 所以 clang 接受这个是错误的吗?我认为可以使用统一初始化(不是初始化列表)来初始化返回类型。
  • 我们应该向委员会写一份请愿书,要求它对所有艺术家都不是明确的> 1...
  • @JonathanWakely:虽然我同意return 语句是explicit 足以选择要选择的类型,但我仍然认为std::tuple 的构造函数没有理由成为explicit arities > 1.
  • @JonathanWakely:我没有把pair 当作一个好的设计的例子,我相当质疑为什么pair 和应该是一个概括的数据结构之间存在这样的差异的pair。我同意构造函数应该是明确的 1 元组。
  • @JonathanWakely:另一方面,如果您有一个接受元组 (foo(tuple&lt;int, double, string&gt;)) 的函数,我希望能够这样称呼它:foo({3, 3.0, "3"})
【解决方案2】:

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

Daniel Krügler 和我大约一年前就这个主题进行了一次对话,他让我相信这个问题值得在 libc++ 中进行扩展以获得现场经验。到目前为止,反馈是积极的。但是我想明确一点:这并不像从 ctor explicit constexpr tuple(UTypes&amp;&amp;...) 中删除 explicit 那样简单。

Daniel 的计划是为tuple 提供一个完全尊重每个元素的隐式/显式构造的构造函数。并且如果每个元素都将从初始化列表中的每个参数隐式构造,那么元组构造是隐式的,否则它将是显式的。

例如:

给定:

#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 提出了一个很好的建议,我更新了这个答案:

自从给出这个答案后,Daniel Krügler 写了一封 paper 并于今年四月将其提交给布里斯托尔的 C++ 委员会。虽然这篇论文很受欢迎,但它在一周内被审查得太晚,无法将其投票纳入当前的工作草案。

更新

Daniel 的提案现在是current working draft 的一部分。在这方面,libc++ 实现将成为下一个 C++ 标准(我们希望是 C++17)的标准。

【讨论】:

  • 有没有办法在 libc++ 中禁用此类扩展,以帮助保持代码的可移植性?
  • 暂时没有。但是请注意,移植工作是一个编译时错误,然后是一个微不足道的修复。
  • 你维护一个 libc++ 扩展列表吗?我在快速浏览 libc++ 源代码时没有看到任何东西,而且我知道您做了很多实验。 --- 确实,一次性的移植工作是微不足道的,但如果一个人在六个平台上与构建机器人进行持续集成,那么让您的工作平台标记事物会更方便。并不是说我认为这值得特别关注;如果 libc++ 是开源的真的很重要,我可以自己解决这个问题......
  • 不,我不知道,这是个好建议。虽然在我的脑海中我想不出除了这个之外的另一个。哦,allocator&lt;T&gt;::propagate_on_container_move_assignmenttrue_type。有时我只是生气。 ;-) 虽然这个已经进入下一个标准的 WP:cplusplus.github.com/LWG/lwg-defects.html#2103
  • 我认为值得一提的是答案中的proposal
猜你喜欢
  • 2013-05-14
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多