23

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

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

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

错误:第 22 行:从初始化程序 \ list 转换为 'std::tuple' 将使用显式构造函数 '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 回答。

4

2 回答 2

34

与 for 不同pair<>,不幸的是,a 的隐式构造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在返回时显式构造 your ,则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;
}
于 2013-02-19T16:02:04.123 回答
24

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

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

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
}

因为从Ato的转换B是隐式的。但是,以下是编译时错误:

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

因为从Ato的转换C是明确的。对于多元素元组,此逻辑继续。为了发生隐式转换,每个元素都必须从参数列表中进行隐式转换:

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 写了一篇论文,并于今年四月提交给了布里斯托尔的 C++ 委员会。尽管这篇论文很受欢迎,但它在一周内被审查得太晚,无法将其投票纳入当前的工作草案。

更新

丹尼尔的提议现在是当前工作草案的一部分。在这方面,libc++ 实现将成为下一个 C++ 标准(我们希望是 C++17)的标准。

于 2013-02-19T16:54:36.917 回答