В стандарте говорится, что std::tuple
имеет следующие функции-члены
constexpr tuple();
explicit tuple(const Types&...);
Может кто-нибудь объяснить, что должно произойти для std::tuple<>
?
В стандарте говорится, что std::tuple
имеет следующие функции-члены
constexpr tuple();
explicit tuple(const Types&...);
Может кто-нибудь объяснить, что должно произойти для std::tuple<>
?
Я считаю, что это небольшая ошибка в стандарте. Очевидно, что когда пакет параметров 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 (реляционные операторы), хотя: "Для любых двух кортежей нулевой длины [...]". К сожалению!: -)
Я предполагаю, что определение, данное в стандарте, должно быть псевдокодом. Так обстоит дело со многими определениями в стандарте; он содержит несколько требований, которые приводятся устно, но являются выполнимыми только с трюками типа 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>());
}
На первый взгляд, двусмысленность будет иметь значение только в том месте, где она называется, а затем у вас нормальное разрешение перегрузки.