Class 模板别名不应该在生成的符号中可见,是吗?
Class templates alias should not be visible in generated symbols, do they?
在 godbolt 中编译此代码段时,大多数编译器生成两种不同的 get
方法(程序集中的不同符号 window):
template<typename T>
struct Field { T impl; };
template<typename T>
using CurrentField = Field<T>;
template<template <typename> class F>
struct Encompassing { F<int> member; };
auto get(Encompassing<Field> const& instance)
{
return instance.member.impl;
}
auto get(Encompassing<CurrentField> const& instance)
{
return instance.member.impl;
}
我在符号中看到 CurrentField
,即使它是别名。只有 gcc 抱怨重新定义(如预期的那样)。
type_alias 上的 C++ 参考说
It does not introduce a new type
所以我认为它不应该像这样,我错了吗?
实际上,大多数编译器的行为似乎是将别名模板替换为 class 特征,例如
template<typename T>
struct CurrentField
{
alias type = Field<T> ;
};
编辑:
这是我在 Godbolt 上尝试实现的更具代表性的示例。请注意,它编译是因为只有一个源并且没有 linking,但 msvc 程序集显示它生成了预实例化签名和用户调用签名。
有4个部分:
1. 具有多种模板的容器库,如 StackField
和 HeapField
,
2. 一个实用程序库,其中包含大小等成员方法,并将字段作为模板参数(如您提出的第二个解决方法),
3. 不同领域的实现在c++中被隐藏和预实例化
4. 用户 link 他们针对这个库的应用程序 A 和 B,使用像 AField
和 BField
这样的别名。它适用于 gcc,但 link 在 msvc 中失败,因为我的预实例化实现的签名与用户调用不匹配
别名模板确实没有引入新类型。但是您的 class 模板将模板作为参数,而不是类型。 Field
和 CurrentField
都是模板。所以这归结为 CurrentField
应该算作与 Field
相同的模板还是单独的模板的问题。这是CWG issue 1286. See also 。 GCC遵循第一种解释,clang和MSVC遵循第二种…
一种解决方法似乎是通过辅助模板来打破 CurrentField
到 Field
的直接映射:
template <typename T>
struct CurrentFieldHelper { using type = Field<T>; };
template <typename T>
using CurrentField = typename CurrentFieldHelper<T>::type;
工作示例here
恐怕没有办法实现相反的效果,即让所有编译器将 get(Encompassing<CurrentField> const &)
视为与 get(Encompassing<Field> const &)
相同的函数。在不了解您的实际代码的情况下,很难针对这个特定问题提出解决方法。一个可能适用于您的实际代码的简单解决方案是制作 size
一个解包 instance.member
的函数模板,然后将其转发给执行实际工作的通用函数:
template <template <typename> class F>
auto size(Encompassing<F> const& instance)
{
return size(instance.member);
}
在 godbolt 中编译此代码段时,大多数编译器生成两种不同的 get
方法(程序集中的不同符号 window):
template<typename T>
struct Field { T impl; };
template<typename T>
using CurrentField = Field<T>;
template<template <typename> class F>
struct Encompassing { F<int> member; };
auto get(Encompassing<Field> const& instance)
{
return instance.member.impl;
}
auto get(Encompassing<CurrentField> const& instance)
{
return instance.member.impl;
}
我在符号中看到 CurrentField
,即使它是别名。只有 gcc 抱怨重新定义(如预期的那样)。
type_alias 上的 C++ 参考说
It does not introduce a new type
所以我认为它不应该像这样,我错了吗?
实际上,大多数编译器的行为似乎是将别名模板替换为 class 特征,例如
template<typename T>
struct CurrentField
{
alias type = Field<T> ;
};
编辑:
这是我在 Godbolt 上尝试实现的更具代表性的示例。请注意,它编译是因为只有一个源并且没有 linking,但 msvc 程序集显示它生成了预实例化签名和用户调用签名。
有4个部分:
1. 具有多种模板的容器库,如 StackField
和 HeapField
,
2. 一个实用程序库,其中包含大小等成员方法,并将字段作为模板参数(如您提出的第二个解决方法),
3. 不同领域的实现在c++中被隐藏和预实例化
4. 用户 link 他们针对这个库的应用程序 A 和 B,使用像 AField
和 BField
这样的别名。它适用于 gcc,但 link 在 msvc 中失败,因为我的预实例化实现的签名与用户调用不匹配
别名模板确实没有引入新类型。但是您的 class 模板将模板作为参数,而不是类型。 Field
和 CurrentField
都是模板。所以这归结为 CurrentField
应该算作与 Field
相同的模板还是单独的模板的问题。这是CWG issue 1286. See also
一种解决方法似乎是通过辅助模板来打破 CurrentField
到 Field
的直接映射:
template <typename T>
struct CurrentFieldHelper { using type = Field<T>; };
template <typename T>
using CurrentField = typename CurrentFieldHelper<T>::type;
工作示例here
恐怕没有办法实现相反的效果,即让所有编译器将 get(Encompassing<CurrentField> const &)
视为与 get(Encompassing<Field> const &)
相同的函数。在不了解您的实际代码的情况下,很难针对这个特定问题提出解决方法。一个可能适用于您的实际代码的简单解决方案是制作 size
一个解包 instance.member
的函数模板,然后将其转发给执行实际工作的通用函数:
template <template <typename> class F>
auto size(Encompassing<F> const& instance)
{
return size(instance.member);
}