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

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

Error: 第22行:从初始化列表转换为“std::tuple”将使用显式构造函数“constexpr std::tuple<_T1, _T2>::tuple(_U1&&, _U2&&) [with _U1 = double; _U2 = double; = void; _T1 = double; _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下面的回答。


只是针对你上一个问题进行评论:不,情况并不相同。返回语句是“复制初始化”上下文,而你上一个示例实际上是“直接初始化”。两者之间的区别在于,对于复制初始化,仅考虑非显式构造函数。 - sellibitze
Visual Studio 14 Update 3也接受它。 - Liviu
2个回答

34

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

因此,如果您在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;
}

2
那么clang接受这个是错误的吗?我认为返回类型可以使用统一初始化来初始化(而不是初始化列表)。 - gnzlbg
6
我们应该写一份请愿书给委员会,要求将其在所有元数大于1的情况下变为非显式。 - PlasmaHH
3
虽然我同意return语句在类型选择方面已经足够明确,但我仍然认为当元素数量大于1时,std::tuple的构造函数没有必要是explicit - Matthieu M.
1
@JonathanWakely:我并不是将pair作为良好设计的例子,我更在质疑为什么pair和一个应该是pair泛化的数据结构之间存在如此大的差异。我同意对于1元组,构造函数应该是显式的。 - Andy Prowl
6
另一方面,如果您有一个接受元组的函数(foo(tuple<int, double, string>)),我希望能够以这种方式调用它:foo({3, 3.0, "3"}) - Andy Prowl
显示剩余14条评论

24

Andy Prowl所提供的答案是正确的。我想评论一下libc++实现,但是评论格式不允许我有足够的空间或格式选择。

大约一年前,Daniel Krügler和我就这个问题进行了交谈,他说服我这个问题值得将一个扩展放入libc++中以获取现场经验。到目前为止反馈一直很积极。不过我想要明确一点:这并不像从ctor explicit constexpr tuple(UTypes&&...)中移除explicit那么简单。

Daniel的计划是给tuple一个构造函数,完全尊重每个元素的implicit/explicit构造。如果每个元素都可以从初始化列表中的每个参数隐式构造,那么tuple构造就是implicit,否则它将是explicit。

例如:

假设:

#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的转换是隐式的。然而下面的代码会在编译时出错:

因为从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写了一篇论文并在今年四月份提交给了C++委员会。虽然该论文受到了好评,但由于提交时间过晚,无法在当前工作草案中进行投票。

更新

Daniel的提案现已成为当前工作草案的一部分。libc++实现现在已经被设定为下一个C++标准(我们希望是C++17)中的标准。


有没有办法在libc++中禁用这些扩展,以帮助保持代码的可移植性? - bames53
目前不支持此功能。但请注意,移植所需的工作是一个在编译时出现的错误,随后需要进行微不足道的修复。 - Howard Hinnant
你是否维护一个libc++扩展列表?我在快速查看libc++源代码时没有发现任何信息,而我了解您做了相当多的实验。---确实,一次性移植工作很简单,但是如果正在半打平台上使用构建机器人进行持续集成,那么拥有您的工作平台标志更加方便。并不是说我认为这值得特别关注; 如果这真的很重要,libc++是开源的,我可以自己解决这个问题... - bames53
不,我没有,这是一个好建议。虽然我一时想不出另一个除了这个之外。哦,allocator<T>::propagate_on_container_move_assignmenttrue_type。有时我只是感到烦躁。;-) 不过这个已经被纳入下一个标准的 WP 中了:http://cplusplus.github.com/LWG/lwg-defects.html#2103 - Howard Hinnant
1
我认为在回答中提到提案是值得一提的。 - oblitum
如果每个元素都将隐式地从初始化列表中的每个参数构造出来,则元组构造将是隐式的,否则它将是显式的。你能解释一下为什么这很必要吗?如果某个元素无法隐式构造,则不管元组构造函数是否显式,都会导致编译错误。 - Nikolai

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