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

Является ли это изменение разрешения перегрузки между Clang 3.5 и 3.6 правильным или ошибкой?

Код ниже компилируется в Visual Studio 2013, gcc 4.8, clang 3.4 и clang 3.5 (Apple LLVM 6.0), но не компилируется в clang 3.6 (через Apple LLVM 6.1)

Код - это упрощенная версия сложного класса в нашей кодовой базе, которая является минимальной, необходимой для выявления проблемы.

Суть проблемы в том, что построение копии TYPED_VALUE в 3.6, вычисляет шаблонный оператор преобразования для типа STRING из-за наличия конструктора, который принимает a STRING; это приводит к оценке std::is_constructible, которая приводит к необходимости определения STRING (которую мы не можем здесь предоставить, приведет к циклической зависимости в полном коде).

class STRING;

class TYPED_VALUE
{
public:
    TYPED_VALUE( const TYPED_VALUE& ) = default; // explicit or implicit doesn't make a difference
    TYPED_VALUE( const STRING & ) {}

    template< typename TYPE, typename std::enable_if<!std::is_pointer< TYPE >::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
    operator TYPE( void ) const = delete;
};

class TYPED_STORAGE
{
public:
    TYPED_STORAGE( const TYPED_VALUE &v ) : value( v ) {}

    TYPED_VALUE value;
};

Сообщение об ошибке

/type_traits:2329:38: error: incomplete type 'SICORE::STRING' used in type trait expression
    : public integral_constant<bool, __is_constructible(_Tp, _Args...)>
                                     ^
/main.cpp:348:99: note: in instantiation of template class 'std::__1::is_constructible<SICORE::STRING, const SICORE::STRING &>' requested here
        template< typename TYPE, typename std::enable_if<!std::is_pointer< TYPE >::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
                                                                                                  ^
/main.cpp:349:9: note: while substituting prior template arguments into non-type template parameter [with TYPE = SICORE::STRING]
        operator TYPE( void ) const = delete;
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/main.cpp:355:56: note: while substituting deduced template arguments into function template 'operator type-parameter-0-0' [with TYPE = SICORE::STRING, $1 = (no value)]
        TYPED_STORAGE( const TYPED_VALUE &v ) : value( v ) {}
                                                       ^
/main.cpp:340:11: note: forward declaration of 'SICORE::STRING'
    class STRING;
          ^

Для меня это похоже на ошибку в версии 3.6, в предыдущих версиях разрешение перегрузки определяет, что конструктор копирования лучше всего подходит без необходимости оценивать аргументы шаблона - я попытался понять примечания о разрешении перегрузки в стандарте, но я думаю это меня просто смутило;)

(Это можно устранить, если я реализую конструктор или оператор преобразования явным, но это не то поведение, которое мы хотим)

Любые стандартные эксперты там знают ответ?

4b9b3361

Ответ 1

Я считаю, что Clang корректен для создания этой ошибки:

В разделе [temp.inst] стандарта С++ в параграфе 10 говорится:

Если шаблон функции или специализированная функция шаблона функции используется таким образом, который включает в себя разрешение перегрузки, объявление специализация неявно создается (14.8.3).

Формирование неявной последовательности преобразования, необходимой для ранжирования кандидатов перегрузки для вызова конструктора TYPE_VALUE, требует создания экземпляра оператора преобразования. И использование параметра неполного типа для признака не создает недопустимый тип, поэтому это не сбой замены, это трудная ошибка.

Ответ 2

Конструктор копирования TYPED_VALUE использует ссылку на STRING, он не должен оцениваться.
Я думаю, что это ошибка clang. Я давно не читал новый стандарт С++, однако я не мог убедиться, что он не изменился.

Ответ 3

Шаблоны

создаются как необходимые, и я думаю, что Clang 3.6 реализовал DR, где ему нужно было создать шаблон раньше, чем 3.5.