【问题标题】:Constexpr member function in class template类模板中的 Consexpr 成员函数
【发布时间】:2018-02-17 13:28:23
【问题描述】:

以下代码编译失败:

// template<class>
struct S {
    int g() const {
        return 0;
    }

    constexpr int f() const {
        return g();
    }
};

int main()
{
    S /*<int>*/ s;
    auto z = s.f();
}

例如,GCC 会抱怨:error: call to non-constexpr function ‘int S::g() const’。这是完全合理的。但是,如果我将 S 转换为模板,代码就会编译(使用 MSVC 15.3、GCC 7.1.0、clang 4.0.1 检查)。

为什么? constexpr 在类模板中有什么特殊含义吗?

据我了解,这段代码不正确,但标准不要求编译器产生错误(为什么?)。

【问题讨论】:

  • 一方面,有趣的行为。另一方面,为什么要从 constexpr 函数调用非 constexpr 函数?
  • @AndyG 这只是一个小例子。在我的例子中,S 是一个模板类,它根据模板参数从不同的基类派生而来。 g() 在基类中。然后我只在 some 基类中创建了 f() constexprg() constexprconstexprness 有意义)。当S 派生自非constexpr g() 的基类时,我很困惑地看到没有编译器错误。因此,问题。
  • 最好是这样,因为我们希望能够有一个模板类,其方法可能是 constexpr,具体取决于其模板参数,也可能不是。我不知道标准在哪里允许这样做。

标签: c++ c++11 templates constexpr


【解决方案1】:

每 [dcl.constexpr]

constexpr 函数的定义应满足以下约束:
...
用于初始化返回值(6.6.3、8.5)的每个构造函数调用和隐式转换都应为 常量表达式中允许的那些之一

常量表达式中不允许调用g()。每[expr.const]:

一个条件表达式是一个核心常量表达式,除非它涉及以下之一作为潜在的 评估子表达式...:
— 调用除 [...] constexpr 函数之外的函数

看起来有些编译器可能允许您执行您正在执行的操作,因为 z 未声明为 constexpr,因此不需要在编译时知道该值。如果您将代码更改为

constexpr auto z = s.f();

您会注意到,所有这些编译器都会进入 barf,无论是否是模板。

【讨论】:

  • 零甚至可以用getchar()之类的东西代替。
  • 来自cppreference.comconstexpr 函数必须满足以下要求: ... 至少存在一组参数值,因此函数的调用可以是核心常量表达式的求值子表达式(对于构造函数,在常量初始化器中使用就足够了)(C++14 起)。违反此项目符号不需要诊断。那么问题是,为什么不需要诊断?
  • @Evgeny:很好地抓住了不需要诊断的方面。通常的解释是,强制执行某事要么花费太多时间,要么不可能针对所有输入执行。
  • @AngyG 不产生错误使constexpr 有条件,可以这么说,它似乎非常有用。根据标准可能允许的一些适当措辞。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
相关资源
最近更新 更多