Неожиданно отсутствующие неявно объявленные конструкторы копирования / перемещения

Учтите следующее:

#include <type_traits>

template <typename>
struct F;

template <typename R, typename... As>
struct F<R(As...)>
{
    template <typename F_, std::enable_if_t<
        std::is_invocable_r_v<R, std::decay_t<F_>, As...>>*...>
    F(F_&&) {}

    F() = default;

    template <typename... As_, std::enable_if_t<
        std::is_invocable_v<void(As&&...), As_...>>*...>
    R operator()(As_&&...)
    { return R(); }
};

struct C
{
    F<C()> f_;

    // C(C&&) = default;  // <<< 1
};

int main() {
    F<C(int)> x;
    auto y = x;
}

gcc 7.3.0 не может его скомпилировать (глубоко внутри std::is_invocable_r):

error: invalid use of incomplete type
    ‘struct std::__or_<std::is_void<C>, std::is_convertible<C, C> >’

как делает Clang 5.0.1:

error: no type named 'type' in
    'std::__or_<std::is_void<C>, std::is_convertible<C, C> >'

Из этого я заключаю, что C отсутствует перемещение и копирование конструкторов. Действительно, если мы раскомментируем его конструктор перемещения (1), этот код компилируется.

Я считаю, что требования к ним, которые должны быть явно заявлены, выполнены. Почему нет?

1 ответ

Решение

Я думаю, что здесь:

F<C()> f_;

C является неполным типом. В F, C заменяет параметр шаблона R, который затем используется в качестве аргумента шаблона std::is_invocable_r_v, Стандарт не позволяет использовать неполные типы в качестве аргументов шаблона std::is_invocable_r_v и это приводит к неопределенному поведению. Неопределенное поведение включает, среди прочего, произвольное поведение компилятора во время компиляции.


Обратите внимание, что я не совсем уверен в своем ответе, главным образом потому, что ни F::F конструктор, ни его шаблон operator() создается экземпляр.

Другие вопросы по тегам