由同一模板函数的局部静态变量参数化的模板类型是否应该比较相等?
Should template types parameterized by local static variables from the same template function compare equal?
考虑以下程序 (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:
Two template-ids refer to the same class, function, or variable if:
- their template-names, operator-function-ids, or literal-operator-ids refer to the same template and
- their corresponding type template-arguments are the same type and
- their corresponding non-type template arguments of integral or enumeration type have identical values and
- their corresponding non-type template-arguments of pointer type refer to the same object or function or are both the null pointer value and
- their corresponding non-type template-arguments of pointer-to-member type refer to the same class member or are both the null member pointer value and
- their corresponding non-type template-arguments of reference type refer to the same object or function and
- their corresponding template template-arguments refer to the same template.
我希望:
- 对于
create<T>
的所有实例化,只有一个 static constexpr int const I = 2;
实例,在这种情况下,decltype(one)
与 delctype(two
引用相同的 class ).
- 或者
create<T>
的每个实例都有一个 static constexpr int const I = 2;
的实例,在这种情况下,两者引用不同的 class.
然而,当使用 GCC 或 Clang(任何生成二进制文件的版本)时,main 的结果是 2
表示:
one
和 two
相同 class。
create<T>()::I
的另一个实例。
程序集列表确认创建了 2 个实例:_ZZ6createIsEDavE1I
(又名 create<short>()::I
)和 _ZZ6createIiEDavE1I
(又名 create<int>()::I
)。
根据C++17标准,one
和two
的类型应该相同还是不同?
有趣的变化:用 = sizeof(T)
替换 = 2
导致类型不同(参见 godbolt)。
我认为 clang/gcc 的行为违反了标准(可能是不正确的优化?),根据 [intro.object](强调我的)
Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first
byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if
one is nested within the other, or if at least one is a subobject of zero size and they are of different types;
otherwise, they have distinct addresses and occupy disjoint bytes of storage.
由于对象没有相互嵌套,也不是零大小的子对象,所以它们不能有相同的地址。
这里的(非规范)脚注也很相关:
under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object
at all if the program cannot observe the difference
但是在这种情况下,当对象位于同一机器地址时(即使我们可以保证它们的值相同),是行为差异,所以他们不应该占用相同的地址。
需要注意的是,MSVC always returns 0 与优化级别无关,我认为这是正确的行为。
考虑以下程序 (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:
Two template-ids refer to the same class, function, or variable if:
- their template-names, operator-function-ids, or literal-operator-ids refer to the same template and
- their corresponding type template-arguments are the same type and
- their corresponding non-type template arguments of integral or enumeration type have identical values and
- their corresponding non-type template-arguments of pointer type refer to the same object or function or are both the null pointer value and
- their corresponding non-type template-arguments of pointer-to-member type refer to the same class member or are both the null member pointer value and
- their corresponding non-type template-arguments of reference type refer to the same object or function and
- their corresponding template template-arguments refer to the same template.
我希望:
- 对于
create<T>
的所有实例化,只有一个static constexpr int const I = 2;
实例,在这种情况下,decltype(one)
与delctype(two
引用相同的 class ). - 或者
create<T>
的每个实例都有一个static constexpr int const I = 2;
的实例,在这种情况下,两者引用不同的 class.
然而,当使用 GCC 或 Clang(任何生成二进制文件的版本)时,main 的结果是 2
表示:
one
和two
相同 class。create<T>()::I
的另一个实例。
程序集列表确认创建了 2 个实例:_ZZ6createIsEDavE1I
(又名 create<short>()::I
)和 _ZZ6createIiEDavE1I
(又名 create<int>()::I
)。
根据C++17标准,one
和two
的类型应该相同还是不同?
有趣的变化:用 = sizeof(T)
替换 = 2
导致类型不同(参见 godbolt)。
我认为 clang/gcc 的行为违反了标准(可能是不正确的优化?),根据 [intro.object](强调我的)
Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.
由于对象没有相互嵌套,也不是零大小的子对象,所以它们不能有相同的地址。
这里的(非规范)脚注也很相关:
under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference
但是在这种情况下,当对象位于同一机器地址时(即使我们可以保证它们的值相同),是行为差异,所以他们不应该占用相同的地址。
需要注意的是,MSVC always returns 0 与优化级别无关,我认为这是正确的行为。