gpt4 book ai didi

c++ - 具有部分定义类的模板化 constexpr 函数调用会更改后续结果

转载 作者:行者123 更新时间:2023-12-05 04:29:17 25 4
gpt4 key购买 nike

我有一个结构,其中有一个静态函数的多个重载需要一些 counter<int>作为论据:

struct S {
static void fn(counter<1>);
static void fn(counter<2>);
static void fn(counter<3>);
};

模板化 constexpr函数可用于查找该类中的特定重载:

template <typename T>
inline constexpr size_t count_fns() {
// 'defines_fn' is a type trait, full code in demo link
if constexpr (defines_fn<T, counter<99> >::value) { return 99; }
if constexpr (defines_fn<T, counter<98> >::value) { return 98; }
if constexpr (defines_fn<T, counter<97> >::value) { return 97; }
// [...]
if constexpr (defines_fn<T, counter<3> >::value) { return 3; }
if constexpr (defines_fn<T, counter<2> >::value) { return 2; }
if constexpr (defines_fn<T, counter<1> >::value) { return 1; }
return 0;
}

在普通用法中,count_fns<S>()正如预期的那样返回 3。

但是,添加一个内联静态变量调用 count_fns改变事情:

struct U {
static void fn(counter<1>);
static void fn(counter<2>);
static constexpr size_t C0 = count_fns<U>(); // C0 is 2
static void fn(counter<3>);
};

static_assert(count_fns<U>() == 3, " <-- fails, value is actually 'still' 2");

Godbolt 建议这种行为在编译器(MSVC、gcc、clang)中是一致的: Demo

这是预料之中的,还是 constexpr 解释器的某种未定义行为?


这些是 counter 的定义和 defines_fn :

template <size_t Value>
struct counter {
static constexpr size_t value = Value;
};

template <typename T, typename Arg, class = void>
struct defines_fn { static constexpr bool value = false; };

template <typename T, typename Arg>
struct defines_fn<T, Arg, std::void_t<decltype(T::fn(std::declval<Arg>()))> >
{
static constexpr bool value = true;
};

最佳答案

应该管理此类代码的标准部分是 [temp.point] .不幸的是,它被认为是有缺陷的。 CWG 287

标准在技术上说在你的例子中,count_fns<U>U 的定义中被引用, 实例化点被认为是在 U 的定义之后.然而,这会导致荒谬;例如,如果我们在 U 中有以下声明会发生什么:

static void fn(counter<2 * C0>);

现在看来我们有一个循环依赖。

CWG 287 关注 模板特化。这些问题有点不同,因为 [temp.point] 说(例如)如果您要在 U 的定义中引用类模板特化。那么实例化点将在 U 的定义之前 . (我还没有弄清楚为什么类模板和函数模板的规则不同。)

为了解决这两种情况下的问题,“常识”方法似乎是从类定义内部引用的模板和非模板构造都应该能够看到该类先前声明的成员(这适用于函数和类模板特化)。 (如果引用它们的上下文是一个完整的类上下文,那么它们应该能够看到该类的所有成员。这是否可能或是否会导致其他问题?我不确定。所以我暂时避免这个问题。)

遵循这个原则,如果 C0 的初始化器需要模板专门化,编译器似乎将实例化点放在 C0 声明之前或之后.在它之前还是之后存在实现差异,但无论如何,它都在 C0 之前的成员声明之后。 ,以及在 C0 之后的成员声明之前.所有主要的编译器似乎都同意这一点。

count_fns<U>的第二次实例化, 在 static_assert声明,引用了 defines_fn 的同一组特化类模板作为第一个实例化。 Class 模板特化与函数模板特化不同,不会在每次引用时重新实例化; 第一个 实例化是“缓存的”。参见 [temp.point] p4 和 p7。所以第二次调用count_fns<U>返回与第一个相同的结果。

所以这似乎就是您看到您所看到的行为的原因。在 CWG 287 确定之前,我们不能说这是对还是错。

关于c++ - 具有部分定义类的模板化 constexpr 函数调用会更改后续结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72382432/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com