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



我遇到了一个非常奇怪的编译器错误。由于某些原因,发布的代码确实使用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:

类型别名声明引入了一个名称,该名称可以用作由type-id表示的类型的同义词。它没有引入新的类型,并且它不能更改现有类型名称的含义。那里类型别名声明和typedef之间没有区别公告该声明可以出现在块作用域、类作用域等中,或命名空间范围。

当推导模板模板参数。

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

借用@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>) {}

代码不再编译失败。可以看出,问题是关于模板参数XY的等价性,它们被推导为不同的类型。

只有在引用别名的特殊化时,才会考虑别名模板生成的类型的等效性,如[temp.alias]/2:中所述

当模板id指的是别名模板的专业化时,它等效于通过将其模板参数替换为别名模板的类型id中的模板参数而获得的关联类型

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

T<int>U<int>是等价的,X<T<int>>Z<U<int>>也是等价的,但这个规则没有扩展到别名模板U等价于类模板T(就其本身而言,它们不是专门化的(。

这与别名FieldData和类模板ConcreteData的情况相同。

事实上,有两份缺陷报告,CWG-1286和CWG-1244,提出了别名模板的等效扩展。

最新更新