【问题标题】:Class templates alias should not be visible in generated symbols, do they?类模板别名在生成的符号中不应该是可见的,是吗?
【发布时间】:2019-11-28 10:17:14
【问题描述】:

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. 包含StackFieldHeapField 等多种模板的容器库, 2.具有成员方法的实用程序库,如大小,字段作为模板参数(如您提出的第二个解决方法), 3. 实现在c++中被隐藏和预实例化,用于不同的领域 4. 用户使用AFieldBField 之类的别名将他们的应用程序A 和B 链接到这个库。它适用于 gcc,但在 msvc 中链接失败,因为我的预实例化实现的签名和用户调用不匹配

【问题讨论】:

  • 您可能需要添加 [language-lawyer] 标签。

标签: c++ c++11 templates language-lawyer alias


【解决方案1】:

确实,别名模板不会引入新类型。但是您的类模板将模板作为参数,而不是类型。 FieldCurrentField 都是模板。所以这归结为CurrentField 是否应该算作与Field 相同的模板或单独的模板的问题。这是CWG issue 1286。另见this thread。 GCC 遵循第一种解释,clang 和 MSVC 遵循第二种解释……

一种解决方法似乎是通过帮助模板破坏CurrentFieldField 的直接映射:

    template <typename T>
    struct CurrentFieldHelper { using type = Field<T>; };

    template <typename T> 
    using CurrentField = typename CurrentFieldHelper<T>::type;

工作示例here

恐怕没有办法实现相反的效果,即让所有编译器将get(Encompassing&lt;CurrentField&gt; const &amp;) 视为与get(Encompassing&lt;Field&gt; const &amp;) 相同的功能。在不了解更多实际代码的情况下,很难针对此特定问题提出解决方法。一个简单的解决方案可能适用于您的实际代码,也可能不适用于您的实际代码,那就是将 size 制作为解包 instance.member 的函数模板,然后将其转发给执行实际工作的通用函数:

template <template <typename> class F> 
auto size(Encompassing<F> const& instance)
{
    return size(instance.member);
}

【讨论】:

  • 谢谢,有没有一个简单的解决方法,使用不同的符号会引起明显的链接时间问题。建议定义“一个“简单”别名,其中 SFINAE 条件与引用的模板相同,并且使用所有模板参数。但我不明白
  • @pierrecemontee 我在上面的答案中添加了似乎是一种解决方法。
  • 谢谢,但实际上我的目标是相反的,所有编译器都拒绝像 gcc 这样的重新定义,因为我希望 auto get(Encompassing const& instance) 调用能够优雅地与唯一的 auto 链接get(Encompassing const& instance) 定义
  • @pierrecemontee 由于某些编译器会将Encompassing&lt;CurrentField&gt; 视为一个单独的、不相关的类型,因此我一般想不出一个好的解决方案。至少如果我们假设您想要应用它的真实代码比您上面的示例更复杂,那么至少不会。我为我的答案添加了一个解决方法的想法,但我不知道它是否适用于您的实际代码......
  • 我有 1. 具有多种模板的容器库,例如 Field,2. 具有成员方法的实用程序库,例如 size,字段作为模板参数(就像您提出的第二种解决方法),3 . 实现在 C++ 中针对不同的字段隐藏和预实例化 4. 用户使用类似于 CurrentField 的别名将他们的应用程序链接到该库。它适用于 gcc,但在 msvc 中链接失败,因为我的预实例化实现的签名和用户调用不匹配
猜你喜欢
  • 2021-12-18
  • 1970-01-01
  • 2012-02-04
  • 2011-05-11
  • 1970-01-01
  • 2018-11-28
  • 2020-01-15
  • 2012-11-09
相关资源
最近更新 更多