Почему `initializer_list <pair>` и `initializer_list <tuple>` ведут себя по-другому?

Следующий код компилируется и запускается:

#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 утверждает, что

Пары - частный случай кортежа.

Итак, почему один код работает, а другой нет?


Дополнительная информация

Ошибка, выводимая 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} } } );
                                   ^

Ответы

Ответ 1

cplusplus.com - не очень хороший сайт, потому что он заполнен ложным утверждением, например: "Пары - частный случай кортежа". Вместо этого вы можете использовать cppreference. На самом деле пара не является частным случаем кортежа.

Теперь считается, что tuple - лучший дизайн; pair намного старше и теперь не может быть изменен из-за обратной совместимости.

Сообщение об ошибке указывает, что разница состоит в том, что tuple имеет конструктор explicit, но pair не работает.

Это означает, что вы должны указывать имя класса при построении кортежа:

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

Это будет изменено в С++ 17: конструктор tuple будет явным, если и только если один из типов кортежей является классом с явным конструктором. gcc 6 уже реализует эту функцию. (Кредит - Джонатан Вакели). См. N4387