这是一个错过的优化机会吗?

Is this a missed optimization opportunity or not

我发布了 this 答案。代码:

#include <atomic>
#include <utility>
void printImpl(...);

std::atomic<bool> printLog = false;

class Log {
 public:
  template <typename T>
  const auto& operator<<(T&& t) {
    if (printLog) {
      ulog.active = true;
      return ulog << std::forward<T>(t);
    } else {
      ulog.active = false;
      return ulog;
    }
  }

 private:
  struct unchecked_log {
    template <typename T>
    const auto& operator<<(T&& t) const {
      if (active) {
        printImpl(std::forward<T>(t));
      }
      return *this;
    }
    bool active{false};
  };
  unchecked_log ulog{};
};

// Instead of the macro. Doesn't break backward compatibility
Log LOG;

void test(bool) { LOG << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10; }

本质上,代码忽略或记录所有数据。想法是在常规 bool 中记录 atomic<bool> ,这样可以更容易地优化掉。我认为大多数编译器可以轻松优化 if (active) 部分,因为它无法在调用之间更改。 Turns out 但是,大多数编译器会将函数调用内联到 unchecked_log::operator<< 但不会优化分支。有什么东西阻止了这种优化吗?会不会违法。

LOG是一个有外部链接的全局变量。因此 printImpl 在另一个翻译单元中的定义可以到达它并且可以在调用之间修改 LOG.ulog.active

LOG设为test中的局部变量,重复的检查将在test的入口处合并为一个,或者将LOG留在原处并make它 static,因此包含 printImpl 定义的不同编译单元无法访问此翻译单元的实例。

正如下面评论中提到的,或者让 operator<< return 复制,这样 return 实例(现在是临时的)对于 [=11= 是不可访问的].


请注意,ulogulog.active 的可访问性(private 等)无关紧要。一旦 printImpl 能够获得相关实例的指针或引用,无论如何 private 都无法防止修改。以下是如何实现的几个示例(非详尽无遗):

  1. LOG 上调用 operator<< 现在可能会根据 printLog 的干预修改或通过 const_cast 结果 [=55] 更改 LOG.ulog.active =]
  2. 调用默认的 copy/move 赋值运算符
  3. (因为这是标准布局 class)reinterpret_cast LOG 对其 ulog 成员的引用
  4. (因为 classes 是可以简单复制的)memcpy 一个不同的状态进入 LOG
  5. placement-new Log 的新实例到 LOG 中,这将使之前的引用自动引用新对象,因为 Log 满足
  6. 的条件]
  7. 等等