仅在首次使用变量时计算变量的最佳模式是什么?

Kel*_*lno 51 c++

这不是一个实际问题,但我正在寻找一种模式来改善以下逻辑:

void PrintToGameMasters()
{
    std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message);
}
Run Code Online (Sandbox Code Playgroud)

该代码有效,但我遇到的问题是,在大多数情况下,没有任何gamemasters参与者,因此消息编写将一事无成。

我想写一些只会在第一次使用该变量时就创建消息的东西,但是在这里我无法提出一个好的解决方案。

编辑:为了使这个问题更精确,我正在寻找一个不是特定于字符串的解决方案,它可能是一个没有函数来测试它是否已初始化的类型。如果我们可以将调用保持在GetComplicatedDebugMessage循环的顶部,这也是一个很大的奖励点,我认为一个涉及包装器的解决方案可以解决这个问题。

Jar*_*d42 77

尽管std::string具有空值,这可能意味着“未计算”,但是您可以更一般地使用std::optional处理空字符串和非默认可构造类型的值:

void PrintToGameMasters()
{
    std::optional<std::string> message;

    for (Player* player : GetAllPlayers()) {
       if (player->IsGameMaster()) {
           if (!message) {
              message = GetComplicatedDebugMessage();
           }
           player->SendMessage(*message);
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,此答案在清晰度,效率和可扩展性之间取得了最佳平衡。 (9认同)
  • 很好 它消除了对“空==尚未计算”的依赖,绝对没有源代码行的开销。实际上,把它弄湿,这就是我要做的。 (5认同)
  • 还是不太漂亮。看到这是一个意见问题吗? (4认同)
  • @ruohola编码标准是高度主观的,此处的一般指导原则是忽略任何可能不得不评论或更改的强制性规定(至少当它们遵循诸如此答案中的标准之类的常用编码标准时)。 (2认同)

rus*_*tyx 38

使用面向数据的设计;保留两个玩家列表:游戏大师和非游戏大师(或像您现在一样的所有玩家,以及指向游戏大师的指针的单独向量)。

void PrintToGameMasters()
{
    auto players = GetGameMasters(); // Returns ONLY game master players
    if (players.begin() != players.end()) {
        std::string message = GetComplicatedDebugMessage();
        for (Player* player : players) {
            player->SendMessage(message);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

目标是最大程度地减少if循环内的陈述。

针对最常见的情况(而不是最普通的情况)进行优化;最常见的情况是玩家不是游戏高手;所以避免循环。


PS:由于您正在开发游戏,因此我想将此链接添加到Mike Acton的cppcon演讲中,您可能会觉得很有趣。

  • 更好的是`if(players.begin()== players.end()){return; }`,然后您就可以完成实际工作而无需额外的缩进块。 (9认同)

Lig*_*ica 36

这里有一些好主意,但我想使其更简单一些:

void PrintToGameMasters()
{
    std::string message;

    for (Player* player : GetAllPlayers())
    {
       if (player->IsGameMaster())
       {
           if (message.empty())
              message = GetComplicatedDebugMessage();

           player->SendMessage(message);
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

每个人都可以遵循它,并且它像芯片一样便宜……再加上调试起来也很容易。

  • 大概适合这种情况。但是总的来说,如果“消息”碰巧是空的,但计算仍然很昂贵怎么办? (4认同)
  • @sebrockm然后,您可以引入`bool hasMessage`。无论哪种方式,这都是非常琐碎的逻辑。 (4认同)
  • 这既简短又简单。+投票确认何时不使用“酷”的东西 (3认同)

Nat*_*ica 30

您可以std::call_once在第一次找到游戏大师时使用lambda来调用该函数

void PrintToGameMasters()
{
    std::once_flag of;
    std::string message;
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
       {
           std::call_once(of, [&](){ message = GetComplicatedDebugMessage(); });
           player->SendMessage(message);
       }
}
Run Code Online (Sandbox Code Playgroud)

  • 我也喜欢它,但是该机制是专门为处理并发问题而设计的(例如:确保多个线程仅调用一次)。与之类似,它带有互锁的开销(开销可能仍然很小,但成本非零)。而call_once的一种廉价替代方案(不是旨在处理并发性的替代方案)只是在boon_flag所在的布尔中,以及if(!hasBeenCalled){hasBeenCalled = true;的构造。fn(); }`&lt;-这就是您所需要的。但是std :: call_once太方便了,无法传递。请注意它的用途。 (32认同)
  • @Wyck Totaly同意。我只是想炫耀一下。Orbit回答中的“轻度竞赛”是提高效率的最佳选择。 (2认同)
  • 真好!这就是我所希望的答案。由于锁的开销,它似乎不适合作为始终重用的通用解决方案,但是很高兴知道。 (2认同)

Nik*_* C. 13

将消息包装在可变的lambda中:

auto makeMessage = [message = std::string()]() mutable -> std::string&
{
    if (message.empty()) {
        message = GetComplicatedDebugMessage();
    }
    return message;
};

for (Player* player : GetAllPlayers())
   if (player->IsGameMaster())
       player->SendMessage(makeMessage());
Run Code Online (Sandbox Code Playgroud)


小智 5

std::optional您可以扩展使用(如 Jarod41 的答案)的方法,并在顶部进行惰性评估。这也将满足“将调用保持GetComplicatedDebugMessage在循环顶部”的要求。

template <typename T>
class Lazy : public std::optional<T> {
public:
    Lazy(std::function<T()> f) : fn(f) { }
    T operator*() {
        if (!*this)
            std::optional<T>::operator=(fn());
        return this->value();
    }
private:
    std::function<T()> fn;
};

void PrintToGameMasters()
{
    Lazy<std::string> message(GetComplicatedDebugMessage);
    for (Player* player : GetAllPlayers())
        if (player->IsGameMaster())
            player->SendMessage(*message);
}
Run Code Online (Sandbox Code Playgroud)


0x5*_*453 4

不确定这是否是最佳模式,但您可以使用 lambda 延迟计算:

void PrintToGameMasters()
{
    std::string message = "";
    auto getDebugMessage = [&message]() -> const std::string& { 
        if (message.empty()) {
            message = GetComplicatedDebugMessage();
        }
        return message;
    };

    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(getDebugMessage());
}
Run Code Online (Sandbox Code Playgroud)