Шаблон псевдонимов конфликтующих типов.g ++ успешно компилируется, а clang не работает - PullRequest
0 голосов
/ 26 ноября 2018

Я столкнулся с очень странной ошибкой компилятора.По какой-то причине опубликованный код правильно компилируется с g ++ (7.3.0), а clang (7.0.0) дает сбой:

../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
        freeFunc(new Func, dummyField);
        ^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
    helper.func();
           ^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
            ^

Для обоих параметров компилятора было задано -std = c ++ 14

template<typename T>
struct ConcreteData
{
    T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
    FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
                     ConcreteField<T, FieldData>& field)
{
    // apply the func on data
    (void)field; // silence compiler warning
    delete func;
}


template<
        typename ScalarType,
        template<typename U> class FieldDataType,
        template<typename U, template <typename X> class Data> class FieldType
        >
struct Traits
{
    using Scalar = ScalarType;

    template<typename T>
    using FieldData = FieldDataType<T>;

    using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
    // using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
    // alias all types given by trait for easier access
    using Scalar = typename Traits::Scalar;
    using Field = typename Traits::Field;

    template<typename U>
    using DataAlias = typename Traits::template FieldData<U>;

    void func()
    {
         // using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
         using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


        Field dummyField;
        freeFunc(new Func, dummyField);
    }
};


int main()
{
    using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
    Helper<ConcreteTraits> helper;
    helper.func();

    return 0;
}

Согласно cppreference.com:

Объявление псевдонима типа вводит имя, которое можно использовать как синоним для типа, обозначаемого идентификатором типа.Он не вводит новый тип и не может изменить значение имени существующего типа.Нет разницы между объявлением псевдонима типа и объявлением typedef.Это объявление может появляться в области блока, области класса или области пространства имен.

и

Шаблоны псевдонимов никогда не выводятся путем вывода аргумента шаблона при выводе параметра шаблона шаблона.

В моем понимании оба типа (ConcreteData и FieldData) должны быть эквивалентны.Почему в этом состоянии происходит сбой clang и почему оба компилятора терпят неудачу при использовании псевдонима «второй этап»?Какой компилятор прав в соответствии со стандартом C ++?Это ошибка компилятора или тонкая неоднозначная интерпретация стандарта C ++ 14?

1 Ответ

0 голосов
/ 26 ноября 2018

Заимствование минимального примера @ Oktalist.

template <typename>
class T {};

template <typename _>
using U = T<_>;

template <template <typename> class X>
void f(A<X>, A<X>) {}

, если вы замените f на:

template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}

, код больше не будет скомпилирован.Вы можете видеть, что проблема заключается в эквивалентности параметров шаблона X и Y, они выводятся для разных типов.

Эквивалентность типов, создаваемых шаблоном псевдонимов, рассматривается только при обращении к специализациипсевдоним, как указано в [temp.alias] / 2 :

Когда идентификатор шаблона относится к специализации шаблона псевдонима , онэквивалентен связанному типу, полученному путем подстановки его аргументов шаблона для параметров шаблона в идентификаторе типа шаблона псевдонима

Использование этого правила и правил эквивалентности [temp.type] / 1 :

T<int> и U<int> эквивалентны, равно как и X<T<int>> и Z<U<int>>, но это правило не распространяется на шаблон псевдонима U, являющийсяэквивалентен шаблону класса T (сами по себе они не являются специализациями).

Это тот же сценарий для псевдонима FieldData и шаблона класса ConcreteData.

Есть на самом деле дваo отчет о дефектах, CWG-1286 и CWG-1244 , в которых предлагается расширение эквивалентности для шаблонов псевдонимов.

...