模板别名冲突类型.当clang失败时,g ++成功编译

sro*_*men 10 c++ templates type-traits template-aliases c++14

我遇到了一个非常奇怪的编译错误.出于某种原因,发布的代码使用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,
            ^
Run Code Online (Sandbox Code Playgroud)

两个编译器选项都设置为-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;
}
Run Code Online (Sandbox Code Playgroud)

根据cppreference.com:

类型别名声明引入了一个名称,该名称可以用作type-id表示的类型的同义词.它不会引入新类型,也不能更改现有类型名称的含义.类型别名声明和typedef声明之间没有区别.此声明可能出现在块作用域,类作用域或命名空间作用域中.

在推导模板模板参数时,别名模板永远不会通过模板参数推导推断出来.

在我的理解中,两种类型(ConcreteData和FieldData)应该是等价的.为什么clang在这种情况下失败?为什么两个编译器在使用"第二阶段"别名时都会失败?根据C++标准,哪个编译器是正确的?它是编译器错误还是对C++ 14标准的微妙模糊解释?

Jan*_*ans 7

借用@Oktalist的最小例子.

template <typename>
class T {};

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

template <template <typename> class X>
void f(A<X>, A<X>) {}
Run Code Online (Sandbox Code Playgroud)

如果你替换f为:

template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
Run Code Online (Sandbox Code Playgroud)

代码不再无法编译.您可以看到问题与模板参数的等效性有关,X并且Y它们被推导为不同类型.

别名模板生成的类型的等效性仅在引用别名的特化时才考虑,如[temp.alias]/2中指定的那样:

当template-id 引用别名模板的特化时,它等效于通过在别名模板的type-id中替换模板参数的template-arguments而获得的关联类型.

使用此规则和等效规则[temp.type]/1:

T<int>并且U<int>是等价的,所以是X<T<int>>Z<U<int>>,但是这个规则没有扩展到别名模板U等同于类模板T(它们本身,它们不是特化).

这与别名FieldData和类模板的方案相同ConcreteData.

实际上有两个缺陷报告CWG-1286CWG-1244提出了别名模板的等价扩展.