【问题标题】:Should template types parameterized by local static variables from the same template function compare equal?由来自同一模板函数的局部静态变量参数化的模板类型是否应该比较相等?
【发布时间】:2020-12-15 13:11:32
【问题描述】:

考虑以下程序 (godbolt):

template <typename, typename>
struct is_same { static constexpr bool value = false; };

template <typename T>
struct is_same<T, T> { static constexpr bool value = true; };

template <typename T, typename U>
static constexpr bool is_same_v = is_same<T, U>::value;

using uintptr_t = unsigned long long;

template <int const* I>
struct Parameterized { int const* member; };

template <typename T>
auto create() {
    static constexpr int const I = 2;

    return Parameterized<&I>{ &I };
}

int main() {
    auto one = create<short>();
    auto two = create<int>();

    if (is_same_v<decltype(one), decltype(two)>) {
        return reinterpret_cast<uintptr_t>(one.member) == reinterpret_cast<uintptr_t>(two.member) ? 1 : 2;
    }

    return 0;
}

基于n4659(C++17最终工作草案):

§ 17.4 [temp.type]/1

两个模板 ID 引用相同的类、函数或变量,如果:

  • 它们的模板名称、操作符函数 ID 或文字操作符 ID 引用相同的模板并且
  • 它们对应的类型模板参数是相同的类型和
  • 它们对应的整数或枚举类型的非类型模板参数具有相同的值并且
  • 它们对应的指针类型的非类型模板参数指的是同一个对象或函数,或者都是空指针值
  • 它们对应的指向成员类型的非类型模板参数指的是同一个类成员,或者都是空成员指针值和
  • 它们对应的引用类型的非类型模板参数引用相同的对象或函数,并且
  • 它们对应的模板模板参数指的是同一个模板。

我希望:

  • 对于create&lt;T&gt; 的所有实例化都存在static constexpr int const I = 2; 的单个实例,在这种情况下decltype(one) 指代与delctype(two 相同的类。
  • 或者static constexpr int const I = 2; 的每个实例都有一个static constexpr int const I = 2; 实例,在这种情况下,这两个实例引用不同的类。

然而,当使用 GCC 或 Clang(任何产生二进制文件的版本)时,main 的结果是 2 表示:

  • onetwo 使用相同的类。
  • create&lt;T&gt;()::I 的另一个实例。

程序集列表确认创建了 2 个实例:_ZZ6createIsEDavE1I(又名create&lt;short&gt;()::I)和_ZZ6createIiEDavE1I(又名create&lt;int&gt;()::I)。

根据C++17标准,onetwo的类型应该相同还是不同?


有趣的变化:将= 2 替换为= sizeof(T) 会导致类型不同(请参阅godbolt)。

【问题讨论】:

标签: c++ c++17 language-lawyer


【解决方案1】:

根据 [intro.object](emphasis 我的),我认为 clang/gcc 的行为违反了标准(可能是不恰当的 as-if 优化?)

除非对象是位域或大小为零的子对象,否则该对象的地址是第一个对象的地址 它占用的字节。两个具有重叠生命周期但不是位域的对象可能具有相同的地址,如果 一个嵌套在另一个中,或者如果至少一个是大小为零的子对象并且它们属于不同类型; 否则,它们具有不同的地址并占用不相交的存储字节

由于对象之间没有嵌套,也不是大小为零的子对象,因此它们不允许具有相同的地址。

这里的(非规范性)脚注也是相关的:

在“as-if”规则下,允许实现将两个对象存储在同一机器地址或不存储对象 如果程序无法观察到差异

但是在这种情况下,当对象位于同一机器地址时,存在行为差异(即使我们可以保证它们的值相同),所以它们不应该占据同一个地址。

需要注意的是,MSVCalways returns 0不管优化级别,我认为这是正确的行为。

【讨论】:

  • 哦!我没有意识到我可以用 Godbolt 测试 MSVC!这是一个非常有趣的数据点,是的,我也相信结果应该是 0——尽管我可以接受 1,因为它至少在类型域和值域之间是一致的。
  • 根据我对 Clang 错误报告的评论,这已在 github.com/llvm/llvm-project/commit/… 中修复(在报告提交当天!)。
猜你喜欢
  • 1970-01-01
  • 2013-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多