gpt4 book ai didi

c++ - 如何让我的类(class)免受 C++ 中的 "auto value = copy of proxy"地雷的影响?

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

我正在处理一个相当复杂的数学库,当客户端代码使用 auto 时,我发现了一个令人讨厌的错误。在创建一个最小的复制案例来询问有关它的问题的过程中,我意识到我可以单独使用标准库来复制类似的东西。看这个简单的测试用例:

#include <vector>
#include <assert.h>

int main()
{
std::vector<bool> allTheData = {true, false, true};

auto boolValue = allTheData[1]; // This should be false - we just declared it.
assert(boolValue == false);
boolValue = !boolValue;
assert(boolValue == true);

assert(allTheData[1] == false); // Huh? But we never changed the source data! Only our local copy.
}
Live on Godbolt 。 (有趣的事实:Clang 实际上将其优化为写入“7” - 3 个真实位 - 以及对 __assert_fail 的调用。)
(是的,我知道 std::vector 很烂 - 但在这种情况下,创建一个只有几行长的最小可重现示例很方便)这是一个 longer example that doesn't use std::vector<bool> ,并使用自定义容器类型,分配和复制/移动已删除,并且仍然显示问题。
我了解幕后发生的事情,operator[] 返回了一个代理类,旨在实现 allTheData[1] = true 和相关功能,编写为好像正在读取值的客户端代码实际上是将代理存储在 boolValue 中,然后当客户端稍后修改它认为是 bool 值的内容,而是修改原始源数据。 TLDR:“自动”复制了代理。
代码做了程序员告诉它做的事情,而不是程序员的意思。
如果程序员想要 boolValue 的更改来更新源数据,他们会完成 auto& boolValue = ... ,它适用于返回 operator[]T& 实现,但不适用于那些需要伪造类似引用行为的自定义代理的实现。
代理的所有复制和移动构造函数以及两个赋值运算符都被声明为私有(private)(也尝试过 = delete ),但是在编译时没有发现这个错误。无论是否删除复制构造函数,都会复制代理。
我为这个错误找到的所有“修复”都集中在代码的客户端部分。它们是:“不要使用自动”、“转换为底层类型”、“通过 const ref 访问”等。这些都是不合标准的修复,一旦你发现不良行为,你可以添加其中之一作为一个黑客修复,但潜在的问题仍然是捕获下一个毫无戒心的用户。
我宁愿移除地雷也不愿继续绕过它,并竖起一个标语“不要使用自动”或“始终使用 const”,只是标记雷区,它不会移除它。
我怎样才能让我的图书馆免受这个问题的影响? (无需更改客户端代码!)
  • 首选是代码按所写的方式工作 - assert(allTheData[1] == false) 通过
  • 一种在代理写入自动时定义其衰减类型的方法?那么 decltype(boolValue)bool 吗?
  • 优先于复制的隐式转换运算符?
  • 还有其他方法可以在不更改上面代码片段的情况下通过吗?

  • 第二个偏好有没有办法使将代理写入变量成为编译错误?
  • 我将复制和移动构造函数声明为删除,将移动和复制赋值运算符声明为删除。仍然编译。
  • 无论如何声明一个类不能成为左值?

  • 提议的 c++ future 标准中有什么可以解决这个问题吗?

  • 还有一个问题是如下代码:
    std::vector<bool> ReadFlags();
    ... later ...
    auto databaseIsLockedFlag = ReadFlags()[FLAG_DB_LOCKED];
    if (databaseIsLockedFlag) <-- Crash here. Proxy has outlived temporary vector.
    我在这里只使用 vector ,因为它是一个非常简单的问题示例。这不是 vector 的错误,这是代理类型模式的错误,其中 vector 是显示问题的示例。
    奇怪的是,MSVC 的 Intellisense 引擎 有时 报告复制 no-move-no-copy 代理类型作为编译错误,但 然后编译它无论如何都很好 :
    enter image description here
    如果这个智能感知编译错误是一个真正的编译错误,那就太好了。叹

    最佳答案

    通过在代理类的 operator= 末尾添加“&&”来减少损坏
    (以及运算符 +=、-= 等)
    花了我很多实验,但我最终找到了一种方法来缓解问题的最常见情况,这会收紧它,所以你仍然可以复制代理,但是一旦你将它复制到堆栈变量,你就不能修改它并无意中损坏了源容器。

    #include <cstdio>
    #include <utility>

    auto someComplexMethod()
    {
    struct s
    {
    void operator=(int A)&& {std::printf("Setting A to %i", A);}
    };
    return s();
    }

    int main()
    {
    someComplexMethod() = 4; // Compiles. Yay

    auto b = someComplexMethod();
    // Unfortunately that still compiles, and it's still taking a
    // copy of the proxy, but no damage is done yet.

    b = 5;
    // That doesn't compile. Error given is:
    // No overload for '=' note: candidate function not viable:
    // expects an rvalue for object argument

    std::move(b) = 6;
    // That compiles, but is basically casting around the
    // protections, aka shooting yourself in the foot.
    }

    关于c++ - 如何让我的类(class)免受 C++ 中的 "auto value = copy of proxy"地雷的影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66930450/

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