Считаются ли std :: tuple и std :: tuple <std :: tuple> одним и тем же типом с помощью std :: vector?

У меня есть переменная, определенная следующим образом

auto drum = std::make_tuple
          ( std::make_tuple
          ( 0.3f
          , ExampleClass
          , [](ExampleClass& instance) {return instance.eGetter ();}
          )
          );

Я ожидаю, что drum будет кортежем кортежа. (т.е. ((a, b, c))).

И у меня есть другая переменная, определенная следующим образом

auto base = std::make_tuple
          ( 0.48f
          , ExampleClass
          , [](ExampleClass& instance) {return instance.eGetter ();}
          );

который я ожидаю, чтобы быть просто кортеж из трех элементов (то есть (a, b, c))

У меня также есть вектор, определенный следующим образом

std::vector<std::tuple<std::tuple< float
                                 , ExampleClass
                                 , std::function<float (ExampleClass&)>
                                 >>> listOfInstruments;

Теперь, если я добавлю drum в listOfInstruments я не ожидаю ошибок.

Что действительно было в случае с listOfInstruments.push_back(drum);

Там, где я ожидал, была ошибка listOfInstuments.push_back(base); но код компилируется просто отлично.

Поскольку listOfInstruments имеет тип "кортеж из кортежей", разве добавление "кортежа" не должно вызывать какую-то ошибку? Если оба () и (()) не считаются одними и теми же типами в std::vector. Или я совершенно не прав и тут что-то еще на работе?

Не могу понять это.

Ответ 1

Кортежи и векторы здесь в основном красные сельди. Работает только то, что push_back, как и любая функция, может выполнять неявные преобразования своего аргумента, как показано в следующем рабочем фрагменте:

#include <vector>

struct A { };

struct B {
    B(A const &) { }
};

int main() {
    std::vector<B> v;
    v.push_back(A{});
}

Возвращаясь к кортежу, мы можем видеть, что он имеет (среди прочих) условно-явный конструктор (здесь # 2), который принимает ссылки на будущих членов кортежа:

tuple( const Types&... args );

Этот конструктор является неявным, если и только если все члены имеют неявные конструкторы копирования, что имеет место в данном случае (поскольку синтезированные конструкторы действительно являются неявными). Это означает, что std::tuple<...> неявно преобразуется в std::tuple<std::tuple<...>>, что вы и наблюдаете.

Ответ 2

Вектор - это красная сельдь. Суть в том, что std::tuple имеет (потенциально) конвертирующий конструктор вида:

template< class... UTypes >
tuple( UTypes&&... args );

В нашем случае пакет содержит один элемент decltype(base). К счастью, он может быть преобразован в один элемент, хранящийся во внешнем кортеже типа векторного значения.