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

具有可变成员的Constexpr对象

邢高澹
2023-03-14

我想出了这个类:

class Point
{
public:
    int X, Y;
    mutable int Z;

    constexpr Point(int x, int y) :X (x), Y(y), Z(0)
    { }

    constexpr int GetX() const
    {
        // Z++; // Wont compile, but following expression is valid!
        return X+Z;
    }

    int GetY() const
    {
        Z++;
        return Y;
    }

    void FoolConst() const
    {
        Z++;
    }
};

这里是用法:

template<int S>
void foo()
{
    std::cout << S << std::endl;
}

int main()
{   
    constexpr Point pt(10, 20);

    pt.FoolConst();

    char arr[pt.GetX()]; // Both compile, but GCC is using extended `new`

    foo<pt.GetX()>(); // GCC fails, VC compiles

    std::cout << sizeof(arr); // 10 (MSVC), 11 (GCC)
    std::cout << pt.GetX();  // 11 (MSVC), 11(GCC)
}

问题:

  • 为什么GetX使用X Z作为返回表达式编译得很好(Z不是instexpr)。
  • 如何调用FoolConstGetY方法的constexpr对象(pt) ?
  • mainGetX的行为在编译器中是不同的。MSVC可以使用int作为模板参数进行编译,而GCC(IdeOne)不会编译它。

对于一个编译器来说< code>constexpr GetX是真正的< code>constexpr,但是对于另一个编译器来说,如果涉及到< code>X Z就不是了。如果我移除< code> Z并简单地< code >返回X GCC就可以了。

我的问题是非常基本的:如果对象是共识,它怎么能调用一个非共识方法呢?

共有3个答案

云瑞
2023-03-14

答案:

为什么GetX使用X Y作为返回表达式编译得很好(Z不是instexpr)。

  • 因为在用constexprhtml" target="_blank">声明之后,整个对象都是常量。对象的常量总是由所有成员派生,除非成员声明为mutable

如何从constexpr对象(pt)中调用FoolConst methods?在编译器中, GetX在main中的行为是不同的。MSVC可以用int作为模板参数编译,而GCC(IdeOne)不会编译它。

    < Li > gcc 4 . 8 . 3对我有效。只要不在< code>GetX()中使用< code>Z,一切都很好,因为使用< code>mutable字段会破坏< code>constexpr(o gcc的行为有点误导,因为它应该在< code>GetX()的定义中报告错误:要么必须定义它,要么不能是< code > const expr ,要么在定义它时不能使用在 < li >如果您有一个编译器,它编译fine,将< code>GetX()的结果作为模板参数传递,该参数引用< code>mutable字段,那么它肯定违反了标准,并且这种行为实际上是未定义的,因为无论z的运行时值发生什么变化,它都应该产生相同的结果(由于它在编译时被解析的事实)。
文嘉禧
2023-03-14

海湾合作委员会就在这里。C 14 标准 [基本型限定符]:

—const对象是const T类型的对象或此类对象的不可变子对象。

所以,在你的例子中,Z是非常量,因此不能用在常量表达式中,就像GCC所说的:

error: mutable 'Point::Z' is not usable in a constant expression
贺乐意
2023-03-14

常量表达式不能访问可变子对象。这在[expr.const]/2中:

条件表达式< code>e是一个核心常量表达式,除非按照抽象机器(1.9)的规则,对e的求值将计算以下表达式之一:[...]

  • 左值到右值转换(4.1),除非它应用于[...]
    • 一个非易失性 glvalue,指的是用 constexpr 定义的非易失性对象,或者是指此类对象的非可变子对象 [...]

    因此< code>GetX不能在常量表达式中使用,例如作为模板参数< code>foo

    在回答您的具体问题时:

      < li >为什么GetX用X Y作为返回表达式编译得很好(Z不是constexpr)。

    编译器不需要检查constexpr函数(包括成员函数)在定义时是否完全有效,只有在使用时才需要检查。它确实需要检查一些事情,比如不使用goto[dcl.constexpr]/3,但不需要检查定义访问哪些对象。这是因为constexpr函数是否可以在常量表达式中使用取决于其参数的值。

    事实上,由于GetX无条件访问Z,您的程序严格按照[dcl.constexpr]/5具有未定义的行为:

    对于非模板的非默认constexpr函数或非模板的、非默认的、非继承的constexpl构造函数,如果不存在参数值,以致函数或构造函数的调用可能是核心常量表达式(5.19)的求值子表达式,则程序格式错误;无需诊断。

    格式不良;“不需要诊断”是程序行为未定义的另一种说法。

      < li >如何从constexpr对象(pt)中调用FoolConst和GetY方法?

    那绝对好;从该对象的非constexpr成员函数的角度来看,声明为constexpr的对象只是一个constobject。

    • 在编译器中,GetX在main中的行为是不同的。MSVC可以用int作为模板参数编译,而GCC(IdeOne)不会编译它

    不幸的是,这两个编译器都是正确的;您的程序在GetX的定义中有未定义的行为,因此编译器没有一个正确的行为。

 类似资料:
  • 问题内容: 我有一类具有各种成员变量的类。有一个构造函数,有getter方法,但没有setter方法。实际上,该对象应该是不变的。 现在我注意到了以下几点:当我使用getter方法获得变量列表时,可以添加新值,依此类推- 可以更改。下次调用此变量时,将返回更改的内容。怎么会这样?我没有再设置它,我只是在做它!使用这种行为是不可能的。那么这里有什么区别? 问题答案: 仅仅因为 对 列表 的引用 是不

  • 我试图创建一个自定义数据类型树。定义如下: 树可以定义为包含一条信息(即,它是一个没有子节点的节点)的叶(由关键字“叶”标识),或包含一条信息的节点(由关键字“节点”标识),再加上树列表–列表中的每个元素表示根在相应子节点上的子树。请注意,根据此定义,树永远不能为空。这意味着树可以是: 叶片数据;或 这是我的代码: 它可以编译,但当我尝试运行时: 而不是预期的输出,我得到以下错误: 我的函数的哪一

  • 我试图在C类中实现一个返回模板参数的constexpr成员函数。代码应该是c 11兼容的。然而,当模板化的类还包含STL容器作为数据成员时,比如STD::vector(const expr成员函数不涉及它),我会遇到编译问题。 以下代码给出了一个最小的示例: 代码使用命令正确编译 g -std=c 14 -O3 快速测试.cpp -o 测试 -Wall clang-STD = c 11-O3 qu

  • 在C 14中,由于<code>constexpr</code>不再是隐式<code>常量</code<,所以<code>constexpr<-code>成员函数是否可以修改类的数据成员:

  • 我很难弄清楚如何使用适当的模板化参数调用setValue函数。在ParameterBase抽象基类中不可能有模板化的参数。非常感谢任何帮助。 附注。我没有使用boost::any的灵活性。

  • 我试图通过一个方法更改对象成员的值,但我不明白为什么它在这种特定情况下不起作用: 当我在全局范围内创建对象时,我不明白为什么值是3。msvc和clang在两种情况下都显示5,但不显示gcc。谁错了?