模板别名冲突类型。 g++ 编译成功而 clang 失败
Template aliases conflicting types. g++ compiles successfully while clang fails
我遇到了一个非常奇怪的编译器错误。由于某种原因,发布的代码确实可以使用 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:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
和
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
根据我的理解,两种类型(ConcreteData 和 FieldData)应该是等价的。为什么 clang 在这种情况下失败,为什么两个编译器在使用 "second stage" 别名时都失败?根据 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>) {}
代码不再编译失败。可以看到问题出在模板参数X
和Y
的等价性上,它们被推导为不同的类型。
别名模板生成的类型的等价性仅在引用别名的特化时才被考虑,如在 [temp.alias]/2:
中指定的那样
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
使用此规则和等价规则 [temp.type]/1:
T<int>
和 U<int>
是等价的,X<T<int>>
和 Z<U<int>>
也是等价的,但是这个规则不会扩展到别名模板 U
等同于 class 模板 T
(就其本身而言,它们不是特化)。
这与别名 FieldData
和 class 模板 ConcreteData
的情况相同。
我遇到了一个非常奇怪的编译器错误。由于某种原因,发布的代码确实可以使用 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:
A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name. There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.
和
Alias templates are never deduced by template argument deduction when deducing a template template parameter.
根据我的理解,两种类型(ConcreteData 和 FieldData)应该是等价的。为什么 clang 在这种情况下失败,为什么两个编译器在使用 "second stage" 别名时都失败?根据 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>) {}
代码不再编译失败。可以看到问题出在模板参数X
和Y
的等价性上,它们被推导为不同的类型。
别名模板生成的类型的等价性仅在引用别名的特化时才被考虑,如在 [temp.alias]/2:
中指定的那样When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
使用此规则和等价规则 [temp.type]/1:
T<int>
和 U<int>
是等价的,X<T<int>>
和 Z<U<int>>
也是等价的,但是这个规则不会扩展到别名模板 U
等同于 class 模板 T
(就其本身而言,它们不是特化)。
这与别名 FieldData
和 class 模板 ConcreteData
的情况相同。