Подтвердить что ты не робот

Confused по умолчанию конструктор описание std:: tuple в стандарте ISO С++

В стандарте говорится, что std::tuple имеет следующие функции-члены

constexpr tuple();
explicit tuple(const Types&...);

Может кто-нибудь объяснить, что должно произойти для std::tuple<>?

4b9b3361

Ответ 1

Я считаю, что это небольшая ошибка в стандарте. Очевидно, что когда пакет параметров Types пуст, два вызова конструктора эквивалентны и не могут быть перегружены (см. С++ 11, раздел 13). (Обратите внимание, что конструктор с использованием Types не является шаблоном-членом либо - если бы он был, то это было бы законной перегрузкой.).

Другими словами, этот код не будет компилироваться:

template <typename... Types>
struct Test
{
  constexpr Test() {}
  explicit Test(Types const&...) { /* etc. */ }
};

int main()
{
  Test<> a;
  Test<int> b;
}

например, выходы моментального снимка g++ v4.8:

tt.cxx: In instantiation of ‘struct Test<>’:
tt.cxx:10:10:   required from here
tt.cxx:5:12: error: ‘Test<Types>::Test(const Types& ...) [with Types = {}]’ cannot be overloaded
   explicit Test(Types const&...) { /* etc. */ }
            ^
tt.cxx:4:13: error: with ‘constexpr Test<Types>::Test() [with Types = {}]’
   constexpr Test() {}
             ^

Это можно исправить с помощью частичной специализации:

template <typename... Types>
struct Test
{
  constexpr Test() {} // default construct all elements
  explicit Test(Types const&...) { /* etc. */ }
  // and all other member definitions
};

template <>
struct Test<>
{
  constexpr Test() {}
  // and any other member definitions that make sense with no types
};

int main()
{
  Test<> a;
  Test<int> b;
}

который будет правильно компилироваться.

Похоже, что стандартный хотел, чтобы конструктор по умолчанию constexpr был таким, чтобы std::tuple<> var; мог быть записан вместо записи std::tuple<> var(); или std::tuple<> var{}; из-за использования explicit с другим конструктором. К сожалению, его определение std::tuple не работает для кортежей с нулевым размером. Стандарт разрешает это в разделе 20.4.2.7 (реляционные операторы), хотя: "Для любых двух кортежей нулевой длины [...]". К сожалению!: -)

Ответ 2

Я предполагаю, что определение, данное в стандарте, должно быть псевдокодом. Так обстоит дело со многими определениями в стандарте; он содержит несколько требований, которые приводятся устно, но являются выполнимыми только с трюками типа enable_if. Это, по-видимому, пример, когда нотация псевдокода на С++ может фактически привести к незаконному С++ при попытке создать такой пустой кортеж (или это может быть просто упущение).

Оба stdlibС++ и libС++ имеют явную специализацию для кортежа нулевого элемента. Например, в stdlibС++:

  // Explicit specialization, zero-element tuple.
  template<>  
    class tuple<>
    {
    public:
      void swap(tuple&) noexcept { /* no-op */ }
    };

с неявным определением однозначного конструктора по умолчанию.

LibС++ явно не объявляет конструктор по умолчанию без параметров. Предположительно, шаблонный конструктор затем выбирается как конструктор по умолчанию для непустых кортежей.

Интересно, что две библиотеки не согласны с тем, какие члены имеют пустой кортеж. Например, следующие компиляции с libС++, но не с libstdС++:

#include <tuple>
#include <memory>

int main() {
  std::tuple<> t(std::allocator_arg, std::allocator<int>());
}

Ответ 3

На первый взгляд, двусмысленность будет иметь значение только в том месте, где она называется, а затем у вас нормальное разрешение перегрузки.