为什么 `initializer_list<pair>` 和 `initializer_list<tuple>` 表现不同?

Why do `initializer_list<pair>` and `initializer_list<tuple>` behave differently?

以下代码编译运行:

#include <initializer_list>
#include <iostream>
#include <vector>
#include <tuple>

void ext( std::initializer_list<std::pair<double, std::vector<double> >> myList )
{
    //Do something
}

///////////////////////////////////////////////////////////

int main(void) {
    ext( { {1.0, {2.0, 3.0, 4.0} } } );
    return 0;
}

虽然这个没有 :

#include <initializer_list>
#include <iostream>
#include <vector>
#include <tuple>

void ext( std::initializer_list<std::tuple<double, std::vector<double> >> myList )
{
    //Do something
}

///////////////////////////////////////////////////////////

int main(void) {
    ext( { {1.0, {2.0, 3.0, 4.0} } } );
    return 0;
}

唯一的区别是,在第一种情况下,ext() 函数接受类型为 initializer_list<pair> 的参数(有效),而另一个使用 initializer_list<tuple>(无效)。然而,cplusplus.com states that

Pairs are a particular case of tuple.

那么为什么一个代码有效而另一个无效?


附加信息

第二种情况clang++输出的错误是:

main.cpp:33:2: error: no matching function for call to 'ext'
      ext( { {1.0, {2.0, 3.0, 4.0} } } );
      ^~~
main.cpp:7:6: note: candidate function not viable: cannot convert initializer list argument to 'std::tuple<double,
      std::vector<double, std::allocator<double> > >'
void ext( std::initializer_list<std::tuple<double, std::vector<double> >> myList )
      ^
1 error generated.

而 g++ 输出:

main.cpp: In function ‘int main()’:
main.cpp:33:35: error: converting to ‘std::tuple<double, std::vector<double, std::allocator<double> > >’ from initializer list would use explicit constructor ‘constexpr std::tuple<_T1, _T2>::tuple(const _T1&, const _T2&) [with _T1 = double; _T2 = std::vector<double>]’
  ext( { {1.0, {2.0, 3.0, 4.0} } } );
                                   ^

cplusplus.com 不是一个很好的网站,因为它充斥着像 "Pairs are a particular case of tuple." 这样的虚假陈述。您可以改用 cppreference。事实上 pair 不是 tuple 的特例。

现在认为tuple是比较好的设计; pair 比较老,现在不能更改,因为向后兼容。

错误消息表明不同之处在于 tuple 有一个 explicit 构造函数,而 pair 没有。

这意味着您需要在构造元组时提及class名称:

 ext( { std::tuple<double,std::vector<double>>{1.0, {2.0, 3.0, 4.0} } } );

这将在 C++17 中更改:tuple 的构造函数将是显式的,当且仅当元组的类型之一是具有显式构造函数的 class 类型时。 gcc 6 已经实现了这个特性。 (信用——Jonathan Wakely)。参见 N4387