在godbolt中编译此片段时,大多数编译器会生成两种不同的get
方法(汇编窗口中的不同符号):
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++引用说
所以我认为它不应该这样表现,我错了吗?
实际上,大多数编译器的行为似乎都像是将别名模板替换为类Trait这样
template<typename T>
struct CurrentField
{
alias type = Field<T> ;
};
编辑:
这是我尝试在Godbolt上实现的目标的更具代表性的示例。请注意,由于只有一个源并且没有链接,所以它可以编译,但msvc程序集显示它既生成了预先实例化的签名,又生成了用户调用签名。
共有4个部分:
1.具有多种模板的容器库,例如
StackField
和HeapField
,2.一个实用程序库,其中包含诸如size之类的成员方法,并且该字段作为模板参数(例如您建议的第二种解决方法),
3.在C++中针对不同的领域隐藏了实现并预先对其进行了实例化
4.用户使用诸如
AField
和BField
之类的别名将应用程序A和B与此库链接。它可以与gcc一起使用,但是在msvc中链接失败,因为我的预实例化实现的签名和用户调用不匹配 最佳答案
别名模板确实不会引入新类型。但是您的类模板采用模板作为参数,而不是类型。 Field
和CurrentField
都是模板。因此,这归结为CurrentField
应该算作与Field
相同的模板还是单独的模板的问题。这是CWG issue 1286。另请参见this thread。 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);
}