当前位置: 首页 > 知识库问答 >
问题:

使用部分定义的类的模板化Constexr函数调用更改后续结果

范浩宕
2023-03-14

我有一个结构,它有一个静态函数的多个重载,采用了一些< code >计数器

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

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

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;
}

在通常情况下,< code>count_fns

但是,添加调用< code>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

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

以下是counterdefines_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;
};

共有1个答案

邹德泽
2023-03-14

标准中用来管理这类代码的部分是[temp.point]。不幸的是,它被认为是有缺陷的。水煤浆287

该标准从技术上说,在您的示例中,其中< code>count_fns

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

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

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

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

遵循这一原则,如果<code>C0</code>的初始值设定项需要模板专用化,编译器似乎会将实例化点放置在<code>CO</code<的声明之前或之后。在实现之前还是之后存在差异,但在任何情况下,都是在C0之前的成员声明之后,而在C0之后的成员声明之前。所有主要的编译器似乎都同意这一点。

count_fns的第二次实例化

所以这似乎就是你看到你所看到的行为的原因。不管是对是错,我们都不能说,直到CWG 287被修复。

 类似资料:
  • 我有一个班级Foo和班级Bar。它们是 std::array 上的包装器。它们都有一些派生类。 并且我想在std命名空间中实现元组接口:get/tuple_size/tuple_element。但是这些方法应该只对Foo可用,并且派生自Foo类。 这有效,但也适用于 Bar 和 Bar 派生类。 我想将std::enable_if与std::is_base_of一起使用。类的std::enable

  • 我的同事今天给我举了以下例子: 在gcc.godbolt.org上运行 null

  • 4.2.4 小结:函数的定义与调用 通过前面的例子,读者应该已经非常熟悉 Python 中函数定义的语法。在此总结如下: def <函数名>(<形式参数>): <函数体> 其中函数名是标识符,命名必须符合 Python 标识符的规定;形式参数是用逗号分隔的变量名序列(可以为空)。函数体是语句序列,左端必须缩进一些空白。 一旦定义了一个函数,就可以在程序的任何地方调用这个函数。函数调用的语

  • 在C 11中,如何专门化一个函数模板,该模板使用decltype声明为“复杂”的尾部返回类型?以下内容适用于GCC,但在VC2013中产生了“错误C2912:显式专业化‘int f(void)’i不是功能模板的专业化”: 我说“复杂”是因为缺乏一个技术上精确的词,因为我不确定是什么造成了差异。例如,以下decltype使用不依赖于任何函数结果类型的内置操作,可以与模板专业化配合使用: 自动f()-

  • 我有一个模板,其中有一个friend函数的声明,在类之外,我有它的实现: 在其他文件中的某个地方将其命名为什么签名? 我尝试过: 但它说它无法解决这个问题。为什么?朋友成员应该这样看待,不是吗? 编辑: 这是在Troll.cpp在它的功能。 仍会喊出“未在此范围内声明”、“函数无法解析”、“符号无法解析”、“之前应为主表达式”、“之前应为主表达式”

  • 每次自定义帖子类型发生变化时,我都想调用一个函数。发布、更新或删除。在该函数中,我从该自定义帖子类型中获取所有帖子,并创建一个我在文件中导出的json文件。 问题是自定义帖子类型有一些我包含在JSON文件中的高级自定义字段。但是,当创建新帖子时,所有ACF都是空的,而标题和创建数据等字段可用。如果我更新帖子,则会获取所有ACF。 我的印象是在ACF存储到数据库之前是挂钩的。我应该使用另一个操作还是