【问题标题】:How can you modify an object without calling member functions?如何在不调用成员函数的情况下修改对象?
【发布时间】:2011-06-24 10:36:51
【问题描述】:

在 3.10/10,标准说:

为了修改对象,对象的左值是必需的,但在某些情况下也可以使用类类型的右值来修改其所指对象。 [示例:为对象(9.3)调用的成员函数可以修改该对象。 ]

因此,除非在某些情况下,否则右值是不可修改的。我们被告知调用成员函数是这些例外之一。这给出了一个想法,即除了调用成员函数之外,还有其他方法可以修改对象。我想不出办法。

如何在不调用成员函数的情况下修改对象?

【问题讨论】:

  • 写对象的字段会不会考虑修改?
  • 难度很大。一方面,您不能使用函数,因为您无法通过调用获得可变引用。
  • @Martinho:你的意思是“这表明你不能在不调用成员函数的情况下修改对象”
  • @Sasha,是的,那当然是修改。 (我怎么会忘记呢?)

标签: c++


【解决方案1】:

如何在不调用成员函数的情况下修改[由右值表达式指定的]对象?

我知道只有一种方法可以做到这一点,即将对象绑定到对const 的引用,然后丢弃const-ness。

例如

template< class Type >
Type& tempRef( Type const& o ) { return const_cast< Type& >( o ); }

struct S { int x; };

int main()
{ tempRef( S() ).x = 3; }

这是因为临时对象本身不是const,除非它是const类型,所以上面的例子并没有抛弃原来的const-ness(应该是UB)。

编辑,补充说:Luc Danton 的回答显示了另一种(非一般)方式,即临时结构将一些引用或指向对象的指针存储在某个可访问的位置。

干杯,

【讨论】:

  • “这是因为临时对象本身不是 const ,除非它是 const 类型”,但只能保证在 C++0x 中是这种情况 - 应该提到。
【解决方案2】:

这个seems to be accepted

struct T {
   int x;
};

int main() {
   T().x = 3;
}

我对此感到有点惊讶,因为 IIRC op= 的 LHS 必须是左值,但以下暗示 even T().x is an rvalue

struct T {
   int x;
};

void f(int& x) {
   x = 3;
}

int main() {
   f(T().x);
}

编辑:从 4.6 开始,GCC 确实警告 T().x = 3: error: using temporary as lvalue

除了通过数据成员访问或成员函数调用之外,我想不出任何其他方式来修改类对象。所以,我要说... 你不能

【讨论】:

  • g++ 在线上说error: using temporary as lvalue [-fpermissive] T().x = 3;
  • @Fred:你使用什么标志? g++ -Wall -Wextra -pedantic 在第一个示例中没有给我任何输出。
  • 还可以通过例如修改适当类型的对象。 std::memset 但由于这需要一个指针,它也不能应用于右值。
  • @Alf:int 的著名复制赋值运算符。
  • @DeadMG:默认情况下不是。为此,您需要 --std=c++0x。
【解决方案3】:

修改一个临时而不是通过该临时的左值:

#include <cstring>

class standard_layout {
    standard_layout();
    int stuff;
};

standard_layout* global;

standard_layout::standard_layout()
{ global = this; }

void
modify(int)
{
    std::memset(global, 0, sizeof *global);
}

int
main()
{
    modify( (standard_layout {}, 0) );
}

我认为假设类类型的右值是不可修改的是不正确的。我现在将该段理解为“对于非类类型,需要一个对象的左值才能修改该对象”。

【讨论】:

  • 然而,这就是标准在 3.10/10 中所说的。你不是在这里使用左值吗?或者对象最初被视为右值是否相关?啊!
  • @Tomalak global 因为它出现在对 memset 的调用中是一个左值,但不是一个左值用于正在修改的对象(即*global),如段落中所示。
  • @Luc:嗯好的。不过,大概在某些时候memset 正在使用*global
  • @Tomalak 我相信使用 char 指针的memset 会符合要求(而且,很明显),*(char*)this 不是*this 的左值。
  • @Luc:“使用”没有左值到右值的转换?
【解决方案4】:

我能想到一种方法:

如果您的类公开了公共成员变量,您可以直接分配给这些成员变量。例如:

class A
{
    public:
        int _my_var;
...
};

int main(int argc, char** argv)
{
    A *a = new C();
    a->_my_var = 10;
}

虽然这不是一种好的编程风格 - 将成员变量公开为我不会提倡甚至建议的事情。

另外,如果你能做一些非常奇怪的事情,比如直接在内存中写入一些地址,从指针到类对象的偏移量——但你为什么要这样做呢?

【讨论】:

  • 如果可以的话,我会把它作为评论
  • "另外,如果你能做一些非常奇怪的事情,比如直接在内存中写入一些地址,从指针到类对象的偏移量" 怎么样?在你有机会之前临时就已经不存在了,它没有内存地址。
  • 这个答案和右值有什么关系?
【解决方案5】:

如何在不调用成员函数的情况下修改对象?

当然,通过为对象的可见数据成员之一赋值。

【讨论】:

    【解决方案6】:

    进行隐式转换有点像调用成员函数——修改右值引用似乎也有效。

    在 vc++10 和 g++ 4.4 中测试了以下内容。

    struct b { int i; b(int x) : i(x) {} };
    struct a { int i; a() : i(0) { } operator b() { return i++ /* this works */, b(i); } };
    a f(a&& x) { return x.i++ /* this works */, x; }
    int main() { b b = f(a()); /* implicit operator b() cast; b.i will equal 2 */ }
    

    【讨论】:

      【解决方案7】:

      成员函数可以直接更改成员,但也可以委派该职责:

      struct Foo {
        int x;
        void Bar() { scanf("%d", &x); }
      };
      

      标准的当前措辞的优点是无需争论这是否是Bar 更改对象的情况。如果我们同意 scanf 更改对象,那么这只是另一个示例。

      【讨论】:

      • 呵呵,你是在调用成员函数。我们不需要争论这是否是Bar 更改对象的情况因为它确实,而不是因为标准中的任何措辞。您将 非常量指针 指向成员。
      猜你喜欢
      • 2020-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-13
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      相关资源
      最近更新 更多