Создайте кортеж, передав один и тот же аргумент каждому элементу с явным конструктором

Следующее отлично работает в Visual C++ 2015 Update 2. Обратите внимание, что A нельзя копировать, а A::A — это explicit.

#include <iostream>
#include <tuple>

struct A
{
    explicit A(int i)
    {
        std::cout << i << " ";
    }

    // non-copyable
    A(const A&) = delete;
    A& operator=(const A&) = delete;
};


template <class... Ts>
struct B
{
    std::tuple<Ts...> ts;

    B(int i)
      : ts((sizeof(Ts), i)...)
    {
    }
};


int main()
{
    B<A, A, A, A> b(42);
}

Цель состоит в том, чтобы передать один и тот же аргумент всем элементам кортежа. Правильно выводит:

42 42 42 42

Однако он не компилируется на g++ 4.9.2. Среди многих сообщений есть перегрузка конструктора tuple, которую, я думаю, следует вызвать:

In instantiation of ‘B<Ts>::B(int) [with Ts = {A, A, A, A}]’:
    33:24:   required from here
    25:30: error: no matching function for call to
       ‘std::tuple<A, A, A, A>::tuple(int&, int&, int&, int&)’
          : ts((sizeof(Ts), i)...)

[...]

/usr/include/c++/4.9/tuple:406:19: note: template<class ... _UElements, class>
    constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...)
     constexpr tuple(_UElements&&... __elements)
               ^
/usr/include/c++/4.9/tuple:406:19: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/tuple:402:40: error: no type named ‘type’ in
    ‘struct std::enable_if<false, void>’
       template<typename... _UElements, typename = typename

Сигнатура функции в сообщении неполная, но она относится к этой:

template<typename... _UElements, typename = typename               
   enable_if<__and_<is_convertible<_UElements,
                   _Elements>...>::value>::type>                        
explicit constexpr tuple(_UElements&&... _elements)                      
     : _Inherited(std::forward<_UElements>(__elements)...) { }    

Насколько я понимаю, is_convertible не работает для явного конструктора. g++ 5.1 и clang 3.5 имеют похожие сообщения об ошибках.

Теперь в С++ 14, 20.4.2.1/10 говорится: «Этот конструктор не должен участвовать в разрешении перегрузки, если каждый тип в UTypes неявно преобразуется в соответствующий тип в Types». Это создает у меня впечатление, что g++ и clang на самом деле имеют такое право, а Visual C++ слишком либерален.

[изменить: похоже, что C++ 17 снял это ограничение, и Visual C++ 2015 следует ему. Теперь он говорит: «Этот конструктор не должен участвовать в разрешении перегрузки, если [...] is_constructible<Ti, Ui&&>::value не является true для всех i». Похоже, что «неявно конвертируемый» был изменен на «is_constructible». Однако мне все еще нужно решение C++14.]

Я попытался удалить explicit из конструктора (я бы предпочел сохранить его). Visual C++ снова компилируется нормально, но и g++, и clang жалуются на удаленный конструктор копирования. Поскольку int теперь неявно преобразуется в A, я, кажется, в конечном итоге

explicit constexpr tuple(const Types&...)

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

Как в C++14 заставить tuple инициализировать свои элементы, передав один и тот же аргумент каждому конструктору, если конструкторы explicit?


person isanae    schedule 12.04.2016    source источник
comment
Это близко к этому, но решения, похоже, не работают для конструкторов explicit.   -  person isanae    schedule 12.04.2016
comment
Похоже на ошибку в libstdc++, работает с libc++ Демо без явного указания и с   -  person Jarod42    schedule 12.04.2016
comment
@ Jarod42 Jarod42 Я прочитал 20.4.2.1/1 так, что libstdc++ действительно прав, не так ли?   -  person isanae    schedule 12.04.2016
comment
Я прочитал неофициальный конструктор кортежей, и требования имеют смысл. Часть !is_convertible должна быть explicit для tuple.   -  person Jarod42    schedule 12.04.2016
comment
sizeof возвращает std::size_t, беззнаковый тип, но вы принимаете подписанный тип int в качестве параметра.   -  person Nowhere Man    schedule 12.04.2016
comment
@NowhereMan sizeof просто для получения контекста, в котором допустимо расширение пакета параметров. Его значение не используется.   -  person isanae    schedule 12.04.2016
comment
@ Jarod42 Я обновил свой вопрос. Похоже, что cppreference ссылается на C++17, в котором изменились требования к этому конструктору.   -  person isanae    schedule 12.04.2016


Ответы (1)


В C++ 14, кажется, нет никакого способа инициализировать элементы кортежа, когда конструкторы явные, из-за требования is_convertible. В итоге я сам написал простую реализацию std::tuple, которая используется в реализациях C++14, таких как Debian 8.

person isanae    schedule 18.04.2016