函数模板特化中T=<未命名命名空间类>的静态局部变量是否需要唯一?

8
我们使用英特尔C++编译器,并检测到它在以下代码中进行了误编译,该代码从使用boost::function<Ponies()> f(unnamedNamespacedFunctor)精简而来。
a1.cc:
template<typename T>
int f(T) { static int x = T::x; return x; }


namespace { struct A { static const int x = 1; }; }

int f1() {
   return f(A());
}

a2.cc:

template<typename T>
int f(T) { static int x = T::x; return x; }


namespace { struct A { static const int x = 0; }; }

int f2() {
   return f(A());
}

main.cc:

#include <cstdio>

int f1();
int f2();

int main() {
   std::printf("%d != %d\n", f1(), f2());
} 

命令行:

# icpc a1.cc a2.cc main.cc -o main
# ./main
0 != 0

我的问题是: 这个代码是否符合要求?在这样的实例化中使用静态局部变量会产生未定义行为吗?当检查生成的符号时,我注意到f具有局部链接,正如我所猜测的那样,而静态变量x接收弱链接,因此两个x被合并,就像抽奖一样随机选择了一个。

# icpc a2.cc a1.cc main.cc -o main
# ./main
1 != 1

我需要帮助,或许这实际上是编译器的一个bug,而且已经被报告了吗?


1
f的定义不违反ODR吗?如果将f的两个定义放在各自的本地命名空间中以修复ODR违规,会发生什么? - MSalters
@MSalters 针对 boost::function 也会出现同样的问题,我不能那么容易地对其进行更改 :) 如果出现 ODR 违规,我猜这是违反了现有惯例。我没有找到任何禁止这种情况的规则。函数模板实例化的链接是外部的,但参数类型具有内部链接(因此在不同的 TU 中是不同的类型!)。我找不到任何禁止它的段落,而且我不确定是否有理由禁止它。 - Johannes Schaub - litb
@MSalters 我只是创建了一个 boost::function<int()> f(MyTuLocalFunctor());,它随机地将一个 .cc 函数对象的行为替换为另一个 .cc 函数对象的行为。结果发现原因是它合并了静态局部变量,而 boost::function 使用这些变量在内部实现其类型擦除。 - Johannes Schaub - litb
如果您确保 f<T> 的实例化不被内联到 f1f2 中(可能需要使用 __attribute__((noinline))),则应该能够看到这些实例化是否具有相同的名称混编。 根据 Itanium C ++ ABI,函数作用域名称的名称混编包括函数的名称混编,我肯定会期望两个实例化具有不同的名称混编。 我怀疑在内联时 icpc 存在错误。 - John Calsbeek
@JohannesSchaub-litb:在函数(模板)内部的静态局部变量,是否可以放在任何匿名命名空间之外?我可以想象,这样名称混淆就无法产生不同的名称。 - MSalters
显示剩余10条评论
1个回答

2

我认为这看起来像是一个错误。让A1A的其中一个实例化,A2是另一个:

我假设静态变量x具有弱链接性,因此链接器可以在多个相同实例化的副本之间合并静态副本。(例如,如果您在两个不同的翻译单元中实例化了f<A1>)

要么f<A1>f<A2>应该有不同的名称编码,这将导致x的两个版本具有不同的名称编码(我想一些编译器实际上会生成一个随机值,使匿名命名空间内的名称唯一),或者x不应该具有内部链接(因为使用了局部类型来实例化f,这应该使它无法在另一个翻译单元中复制)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接