【问题标题】:Implicit instantiation of function templates when taking their address获取地址时函数模板的隐式实例化
【发布时间】:2013-04-24 14:56:32
【问题描述】:

注意:我已经看过here,但我认为答案不正确。

在获取地址时,管理函数隐式实例化的规则是什么? n3242 的 14.7.1/9 是这样说的:

实现不应隐式实例化不需要实例化的类模板的函数模板、成员模板、非虚拟成员函数、成员类或静态数据成员。

现在,当然不需要函数定义来获取它的地址。我们可以获取前向声明函数的地址并将它们定义在不同的翻译单元中。

既然如此,我不知道什么时候需要。然而,编译器似乎有自己的想法。在 GCC 和 VC 上进行测试,这里有几个例子:

template <typename T> void CallBanana() { T::Banana(); }
template <typename T> void CallUnimpl();

template <typename T>
struct S {
    static void CallBanana() { T::Banana(); }
    static void CallOrange() { T::Orange(); }
    static void CallUnimpl();
};

struct B { static void Banana() {} };

int main() {
    (void)(&CallBanana<void>); // 1
    (void)(&CallUnimpl<void>); // 2
    (void)(&S<void>::CallBanana); // 3
    (void)(&S<void>::CallOrange); // 4
    (void)(&S<void>::CallUnimpl); // 5
    (void)(&S<B>::CallBanana); // 6
}

这些应该被注释掉,一次看到效果。

GCC 4.7 tested here 会抱怨 1、3 和 4。因此它会实例化所有定义(如果存在)。

VC 2010(没有在线测试,抱歉)实例化 3 和 4,但不实例化 1。

Clang 3.0 tested here 具有与 VC 2010 相同的行为。

没有编译器抱怨 2 或 5,这是我所期望的。如果我实际使用这些指针,我希望它无法链接。

在所有编译器上,6 次编译。我期待这一点,但它旨在表明整个类模板没有被实例化(如另一个问题的答案中所述),只是因为我获取了单个函数的地址。如果整个模板被实例化,那么 S::CallOrange 不应该编译,因为 B 不包含 Orange 函数。

所以我想知道是否有人对正确的行为应该是什么有明确的答案。该标准似乎声称不应实例化任何函数,但三个流行的编译器在某些情况下会实例化,但彼此也有所不同。

【问题讨论】:

  • 我完全不同意,据我所知,标准要求在获取地址并获取所有 6 个地址时实例化该函数。如果无法实例化函数(1、3 和 4),则会引发编译错误。所以我会说应该实例化 2、5 和 6;并且由于他们的地址被占用(您是否转换为void 无关紧要)他们的缺席应该会引发链接器错误(除非经过优化)。
  • '据我所知' - 在哪里?部分和段落,请。强制转换为 void 只是为了避免关于表达式被用作语句的警告,与我正在做的事情无关。
  • 如果我有部分和段落,这将是一个答案;)我相信它属于 odr-used case。

标签: c++ templates language-lawyer


【解决方案1】:

如果您获取函数的地址(在评估的上下文中),则需要定义函数。

当然可以在单独的翻译单元中给出定义,但这不会改变需要定义的事实。

如果只需要一个成员函数,这并不意味着其他成员函数也会被实例化。

如果函数模板未定义,则不能隐式实例化。然后必须在另一个翻译单元中显式实例化它。不允许依赖另一个翻译单元中的隐式实例化(但不需要诊断)。

【讨论】:

  • 14p6 最后一点,我想。关于您的主要观点,即使调用函数定义也不需要可见,但标准显然打算调用导致实例化函数模板定义;拿一个指针也没什么不同。
  • 14.7.1/2 说“当在需要函数定义存在的上下文中引用特化时,函数模板特化被隐式实例化”。这有点模糊(在我看来),但我想这可以这样解释。如果是这样,Clang 是否因不实例化非成员函数模板而不足? VC 弄错了,我并不感到惊讶。 :)
  • 我已将此标记为已接受的答案,但如果有比“需要存在函数定义”更清晰的解释来查看 VC 和 Clang 的行为是否受到标准。
【解决方案2】:

需要进行实例化,因为模板的扩展可能不会产生格式良好的代码。

事实上,实例化甚至可能不会产生有效的原型(这种失败将被视为“非失败”)。

(当涉及 SFINAE 时,在实际的候选人被选择为其地址之前,所采用的有效地址可能会忽略部分排序中的几个较早的候选人(因为替换失败不是错误)。)

【讨论】:

  • 我认为这是不正确的。 14.7.1/8 说“如果以涉及重载解析的方式使用函数模板或成员函数模板特化,则隐式实例化特化的声明”。请注意,声明是实例化的,而不是定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多