将模板成员函数的专门化定义(没有默认主体)放在源文件中是否安全?

2023-11-23

这就是我的意思:

// test.h
class cls
{
public:
    template< typename T >
    void f( T t );
};

-

// test.cpp
template<>
void cls::f( const char* )
{
}

-

// main.cpp
int main()
{
    cls c;

    double x = .0;
    c.f( x ); // gives EXPECTED undefined reference (linker error)

    const char* asd = "ads";
    c.f( asd ); // works as expected, NO errors

    return 0;
}

这完全没问题,对吧?

我开始怀疑这一点,因为我刚刚跑过specialization of '...' after instantiation错误,这对我来说是新的。所以,我“解决”了这个错误,现在一切似乎都工作正常,但仍然..

这是明确定义的行为吗?


edit:对于非成员模板函数也是如此(前向声明的非成员模板函数)。


轨道上的轻盈竞赛 cited 为什么不合规标准中的零件。附近可能还有其他一些。

我将尝试用更简单的术语解释标准措辞的含义,希望我能正确理解它,最后解释链接器错误(或没有错误):

  1. 实例化的重点是什么?
  2. 编译器如何选择专业化?
  3. 实例化时需要什么?
  4. 为什么会出现链接器错误?

1/ 实例化的意义是什么?

模板函数的实例化点是调用或引用它的点(&std::sort<Iterator>) with all模板参数充实了 (*)。

template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }

int main() { foo(1); } // point of instantiation of "foo<int>(int)"

但对于从其他模板调用的模板,它可能会延迟,因此与确切的调用站点不匹配:

template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }

template <typename T>
void bar(T t) { foo(t); } // not a point of instantiation, T is still "abstract"

int main() { foo(1); } // point of instantiation of "bar<int>(int)"
                       // and ALSO of "foo<int>(int)"

这种延迟非常重要,因为它可以写入:

  • 共同递归模板(即相互引用的模板)
  • 用户专业化

(*) 粗略地说,也有一些例外,例如模板类的非模板方法......


2/编译器如何选择专业化?

在实例化时,编译器需要能够:

  • 决定哪个基础模板要调用的函数
  • 以及可能,它的哪个专业被称为

这个老GotW展示了专业化的困境......但简而言之:

template <typename T> void foo(T);   // 1
template <typename T> void foo(T*);  // 2

are 超载,并且每个都会产生一个独特的family他们是可能的专业base.

template <> void foo<int>(int);

是 1 的专业化,并且

template <> void foo<int*>(int*);

是2的专业。

为了解析函数调用,编译器会首先选择最好的重载,同时忽略模板专业化,然后,如果它选择了模板函数,请检查它是否有任何可以更好应用的专业化。


3/ 实例化时需要什么?

因此,从编译器解析调用的方式来看,我们可以理解why该标准规定任何专业化都应声明before它的第一个实例化点。否则的话,根本就不会考虑。

因此,在实例化时,人们需要已经看到:

  • 要使用的基本模板函数的声明
  • 要选择的专业声明(如果有)

但定义又如何呢?

不需要。编译器假设它将稍后在 TU 中提供或完全由另一个 TU 提供。

注意:它确实给编译器带来了负担,因为这意味着它需要记住它遇到的所有隐式实例化,并且它无法发出函数体,以便当它最终遇到定义时,它可以(最后)发出所有必要的代码它遇到的所有专业化。我想知道为什么选择这种特殊的方法,也想知道为什么即使没有extern声明 TU 可能以未定义的函数体结束。


4/ 为什么会出现链接器错误?

由于没有提供定义,gcc 相信您稍后会提供它,并且只是发出对未解析符号的调用。如果您碰巧链接到另一个提供此符号的 TU,那么一切都会很好,否则您将收到链接器错误。

由于 gcc 遵循安腾ABI我们可以简单地看看它是如何破坏符号的。事实证明,ABI 在破坏专业化和隐式实例化方面没有任何区别,因此

cls.f( asd );

calls _ZN3cls1fIPKcEEvT_(它分解为void cls::f<char const*>(char const*))和专业:

template<>
void cls::f( const char* )
{
}

还生产_ZN3cls1fIPKcEEvT_.

注意:我不清楚是否可以对显式专业化进行不同的修改。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将模板成员函数的专门化定义(没有默认主体)放在源文件中是否安全? 的相关文章

随机推荐

  • PHP 在所有请求中保留变量

    在某些语言 C 或 NET 中 这将是静态变量 但在 PHP 中 每次请求后都会清除内存 我希望该值在所有请求中持续存在 我不想 SESSION 因为每个用户的情况都不同 为了帮助解释这里有一个例子 我想要一个像这样的可以计数的脚本 无论哪
  • 在 File.Create 之后关闭文件[重复]

    这个问题在这里已经有答案了 我检查文件是否存在 if File Exists myPath File Create myPath 但是 当我去创建一个StreamReader使用这个新创建的文件 我收到一条错误消息 该进程无法访问文件 此处
  • 摆脱停用词和标点符号

    我正在与 NLTK 停用词作斗争 这是我的代码 有人可以告诉我出了什么问题吗 from nltk corpus import stopwords def removeStopwords palabras return word for wo
  • 为什么在 HTC Desire 上发送短信时出现 NullPointerException,或者什么是 SubmitPdu?

    所以我得到这个堆栈跟踪 java lang NullPointerException at android telephony SmsMessage SubmitPdu
  • Akka:如何查找集群中的当前节点?

    从 Akka actor 内部 如何找到集群的节点 即本节点认为当前可以访问的节点 谢谢 丹尼尔 您实际上不需要订阅ClusterDomainEvent or MemberEvent 您只需访问state集群扩展的成员 例如 val clu
  • 相同美学的多个传说

    我正在尝试使用facet grid or facet wrap和这个结合geom raster 然而 在每个面板中 z审美是不同尺度的 例如 Data at end of question ggplot dd aes x y geom ra
  • 没有可以捕获托管变量的 lambda 的解决方法

    在 C CLI 中 您无法创建托管 lambda 就像在 C 中一样 因此无法捕获托管变量 您可以创建常规方法 而不是 lambda 但您仍然无法捕获托管变量 C CLI 代码中是否有可采用的标准解决方法 换句话说 我正在寻找一个可以在 C
  • 如何在 symfony2 中禁用数据库配置

    我正在开发的新应用程序正在使用 Symfony2 没有可供使用的数据库连接 相反 它是建立在许多 Web 服务调用之上的 在 Symfony app config 下 我想删除所有 database 条目 但是当我这样做时 我得到一个 Pa
  • 如何在 elastic beanstalk 中扩展 nginx 配置 (Amazon Linux 2)

    我听从了建议here配置 nginx 反向代理以允许大于默认 1mb 的文件 所以 我的代码在 platform nginx conf d prod conf看起来像这样 http client max body size 30M 然而 这
  • 适用于 iPhone 的 UIPOPOVER?或者是假的?

    谁能看一下这个吗 http www woowoomac com storage awesome note iphone note todo app menus jpg SQUARESPACE CACHEVERSION 12685817624
  • 如何让 CRON 调用正确的路径

    我试图让 cron 调用正确的路径 当我从 shell 运行 Python 脚本时 脚本运行良好 因为它使用 bashrc 中设置的 PATH 但是当我使用 cron 时 所有 PATH 都不会从 bashrc 使用 是否有一个文件可以像
  • WCF 4 服务的扁平 WSDL

    使用 WCF 3 5 和来自 Christian Weyer 的 FlatWsdl EndpointBehavior 我能够为我的 WCF 服务获取单个平面 WSDL 文件 而无需任何
  • DDD-- 如何补充水分

    Question 从存储库中重新水化聚合体的最佳 高效且面向未来的方法是什么 所提供的方法有哪些优点和缺点 我的看法是否正确 假设我们有一个带有私有 setter 和公共 getter 的聚合根用于访问state 行为是通过聚合根上的方法完
  • Dynamic_cast 中的类型必须是完整类类型的指针或引用,或者 void *

    我希望有人能理解为什么下面的代码失败 我试图从 osg Node 节点对象获取 PositionAttitudeTransform Openscenegraph 类 的实例 但下面有粗体的编译器错误 void CameraPosCallba
  • 为什么扩展元素不适合复制多维数组?

    来自MDN 传播语法 注意 通常 ES2015 中的扩展运算符在复制数组时会深入一层 因此 它们不适合复制多维数组 这与 Object assign 和对象扩展语法的情况相同 请参阅下面的示例以更好地理解 var a 1 2 3 var b
  • Codeigniter 如何创建 PDF

    我要创建一个发票系统 我现在正在为此做准备 我将使用 Codeigniter 问题是我想创建 PDF 格式的发票 这样我就可以通过电子邮件发送它 你们有什么建议 我正在考虑 HTML 到 PDF 的转换 或者在屏幕上显示发票并安装 pdf
  • Boost 随机和 OpenMP

    我从 OpenMP 并行代码部分收到 总线错误 我在下面重新创建了我的问题的简单版本 该代码本质上对函数进行了多次调用uniform distribution 它使用 Boost 的uniform int distribution 绘制 0
  • SQL Server 2005 - 将列设置为只读

    我在 SQL Server 2005 数据库的表中有一个 InsertTime 字段 当记录首次插入数据库时 该字段默认为 getDate 我想确保此专栏不再更新 是否可以将此列设置为只读 或者是否有更好的方法来执行此操作 而无需为开发人员
  • 使用 Java 的 Google 数据存储模拟器(不使用 GAE)

    我正在使用 Google Cloud 的 Java 数据存储客户端库来访问 Cloud Datastore Note 我没有使用 App Engine 来部署我的应用程序 只是出于开发目的运行本地应用程序 按照示例 我可以读取 写入云数据存
  • 将模板成员函数的专门化定义(没有默认主体)放在源文件中是否安全?

    这就是我的意思 test h class cls public template lt typename T gt void f T t test cpp template lt gt void cls f const char main