Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 什么';只有在第一次使用变量时才计算它是一个好模式吗?_C++ - Fatal编程技术网

C++ 什么';只有在第一次使用变量时才计算它是一个好模式吗?

C++ 什么';只有在第一次使用变量时才计算它是一个好模式吗?,c++,C++,这不是一个实际问题,但我正在寻找一种模式来改进以下逻辑: void PrintToGameMasters() { std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info for (Player* player : GetAllPlayers()) if (player->IsGameMaster())

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

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);
}
这段代码是有效的,但我遇到的问题是,在大多数情况下,没有任何
gamemasters
玩家,因此消息合成将是免费的

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

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

不确定这是否是最佳模式,但可以使用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());
}
您可以使用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);
       }
}

这里有一些好主意,但我想让它更简单一点:

void PrintToGameMasters()
{
    std::string message;

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

           player->SendMessage(message);
       }
    }
}

每个人都可以遵循这一点,而且它像芯片一样便宜……而且调试起来也像馅饼一样简单。

将消息包装在可变的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());
真希望这有帮助

尝试实现以下逻辑:

#include <iostream>

using namespace std;

int main()
{
    bool GameMaster,first_time,used_before;

    GameMaster = true;
    first_time = false;
    used_before = false;

    GameMaster = first_time;
    first_time = used_before;


    for( int i = 0; i < 5; i++ ) {

        if(GameMaster==used_before) {
            cout<<"    First Time";
            GameMaster = true;
        }

        if(GameMaster!=used_before) {
            cout<<"    Old News";
        }
    }

    return 0;
}
使用;保留两个玩家列表:游戏大师和非游戏大师(或者像你现在这样的所有玩家+一个单独的指向游戏大师的指针向量)

目标是最小化循环中的
if
-语句

针对最常见的情况而不是最一般的情况进行优化;最常见的情况是,玩家不是游戏大师;因此,避免在它们上面循环



另外,由于您正在开发一个游戏,我想将此链接添加到Mike Acton的链接,您可能会对此感兴趣。

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);
       }
    }
}
void PrintToGameMasters()
{
std::可选消息;
对于(Player*Player:GetAllPlayers()){
如果(player->IsGameMaster()){
如果(!消息){
message=getComplementedBugMessage();
}
播放器->发送消息(*消息);
}
}
}

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

模板
类:public std::optional{
公众:
惰性(std::函数f):fn(f){}
T算子*(){
如果(!*这个)
std::optional::operator=(fn());
返回此->值();
}
私人:
std::功能fn;
};
作废PrintToGameMasters()
{
惰性消息(GetComplementedBugMessage);
对于(Player*Player:GetAllPlayers())
如果(player->IsGameMaster())
播放器->发送消息(*消息);
}

我不知道为什么要将
消息的定义保持在循环上方。如果有人在阅读代码以分析在std::end(GetAllPlayers())==std::begin(GetAllPlayers)(
)的情况下会发生什么,那么你不希望他们的心理工作区被不相关的变量弄得乱七八糟

如果你愿意放弃,那么
static
就是你的朋友:

void PrintToGameMasters()
{
    for (auto const &player : GetAllPlayers())
        if (player->IsGameMaster())
        {
            //Initialization of a static variable occurs exactly once, even when multithreaded,
            //precisely when the defining line is hit for the first time
            static auto const &message{GetComplicatedDebugMessage()};
            player->SendMessage(message);
        }
}

这很有效。正如麻省理工学院许可证所说:

软件按“原样”提供,无任何形式的担保, 明示或暗示

#包括
#包括
#包括
#包括
结构播放器{
bool-isGameMaster;
int-id;
};
int uu stdcall IsGameMaster(玩家*自身){
返回self->isGameMaster?1:0;
}
//可能是“发送消息”。。。但是Windows
void uu stdcall SendMessageToPlayer(Player*self,std::string*msg){
printf(“玩家%d说:%s\n”,self->id-1000+1,msg->c_str());
}
玩家g_玩家[18];
玩家*\uu stdcall GetAllPlayers(无效){
return&g_玩家[0];
}
std::string le_message=“嗨,我是游戏大师”;
std::string*\uu stdcall getcomplementmessage(void){
puts(“生成复杂消息。占用CPU 3天!”);
return&le_message;//使我的程序集更轻松
}
__declspec(裸版)作废打印相机母版(作废版){
__asm{
推动ebp;
mov-ebp,esp;
亚esp,8;
调用GetAllPlayer;
mov[ebp-4],eax;
//这是“i”,循环迭代计数器
//我选择esi是因为它由stdcall保存
xor esi,esi;
do_循环:
//Player*Player=&g_players[i];
mov-ebx,esi;
imul ebx,尺寸播放器;
添加ebx,[ebp-4];//ebx=g_玩家+sizeof(玩家)*i,或&g_玩家[i]
//如果(player->IsGameMaster()){
推ebx;
呼叫IsGameMaster;
测试eax,eax;
jz无印;
//msg=getcomplementmessage();
获取\u消息\u开始:
调用getcomplementmessage;
mov[ebp-8],eax;
jmp-short-delete_-self;
获取\u消息\u结束:
//播放器->发送消息(msg);
推压[ebp-8];
推ebx;
调用SendMessageToPlayer;
// }
无打印:
公司esi;
cmp-esi,18;
jb-do_环;
mov-esp,ebp;
pop-ebp;
ret;
删除你自己:
mov ecx,获取消息启动;
mov eax,get_msg_end;
子eax、ecx;
mov字节ptr[ecx],0xEB;//jmp短
mov字节ptr[ecx+1],al;//相对偏移量
jmp get_msg_end;
}
}
int main(){
对于(int i=0;i<18;i++){
g|U玩家[i].isGameMaster=(i==12 | | i==15);//玩家13和16
g_玩家[i].id=i+1000;
}
德沃德·奥尔德普雷特;
VirtualProtect(&PrintToGameMasters,0x1000,第页\u执行\u读写,&oldProtect);
PrintToGameMasters();
返回0;
}

它比
if(!message)消息快得多
void PrintToGameMasters()
{
    std::optional<std::string> message;

    for (Player* player : GetAllPlayers()) {
       if (player->IsGameMaster()) {
           if (!message) {
              message = GetComplicatedDebugMessage();
           }
           player->SendMessage(*message);
       }
    }
}
void PrintToGameMasters()
{
    for (auto const &player : GetAllPlayers())
        if (player->IsGameMaster())
        {
            //Initialization of a static variable occurs exactly once, even when multithreaded,
            //precisely when the defining line is hit for the first time
            static auto const &message{GetComplicatedDebugMessage()};
            player->SendMessage(message);
        }
}
#include <Windows.h>
#include <cstdlib>
#include <cstdio>
#include <string>

struct Player {
    bool isGameMaster;
    int id;
};

int __stdcall IsGameMaster(Player* self) {
    return self->isGameMaster ? 1 : 0;
}

// Could've been "SendMessage"... but Windows.h
void __stdcall SendMessageToPlayer(Player* self, std::string* msg) {
    printf("Player %d says: %s\n", self->id - 1000 + 1, msg->c_str());
}

Player g_players[18];
Player* __stdcall GetAllPlayers(void){
    return &g_players[0];
}

std::string le_message = "hi, I'm a game master";
std::string* __stdcall GetComplicatedMessage(void) {
    puts("GENERATING COMPLICATED MESSAGE. HOGGING CPU FOR 3 DAYS!");
    return &le_message; // to make my assembly life easier
}

__declspec(naked) void PrintToGameMasters(void){
    __asm {
        push ebp;
        mov ebp, esp;
        sub esp, 8;

        call GetAllPlayers;
        mov [ebp-4], eax;

        // this is 'i', the loop iteration counter
        // I chose esi because it is preserved by stdcalls
        xor esi, esi;

        do_loop:

        // Player* player = &g_players[i];
        mov ebx, esi;
        imul ebx, SIZE Player;
        add ebx, [ebp-4]; // ebx = g_players + sizeof(Player) * i, or &g_players[i]

        // if (player->IsGameMaster()) {
        push ebx;
        call IsGameMaster;
        test eax, eax;
        jz no_print;

        // msg = GetComplicatedMessage();
        get_msg_start:
        call GetComplicatedMessage;
        mov [ebp-8], eax;
        jmp short delete_self;
        get_msg_end:

        // player->SendMessage(msg);
        push [ebp-8];
        push ebx;
        call SendMessageToPlayer;

        // }
        no_print:
        inc esi;
        cmp esi, 18;
        jb do_loop;

        mov esp, ebp;
        pop ebp;
        ret;

        delete_self:
        mov ecx, get_msg_start;
        mov eax, get_msg_end;

        sub eax, ecx;
        mov byte ptr [ecx], 0xEB; // jmp short
        mov byte ptr [ecx+1], al; // relative offset
        jmp get_msg_end;
    }
}

int main(){
    for (int i = 0; i < 18; i++) {
        g_players[i].isGameMaster = (i == 12 || i == 15); // players 13 and 16
        g_players[i].id = i + 1000;
    }

    DWORD oldProtect;
    VirtualProtect(&PrintToGameMasters, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);

    PrintToGameMasters();

    return 0;
}
void PrintToGameMasters()
{
    struct {
        operator std::string const &(void)
        {
            static auto const real_value{GetComplicatedDebugMessage()};
            return real_value;
        }
    } message;
    for (auto const &player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message);
}
void PrintToGameMasters()
{
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster()) {
           static std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
           player->SendMessage(message);
       }
}
void PrintToGameMasters()
{
    auto message = std::async(
        std::launch::deferred,
        []{return GetComplicatedDebugMessage();}
    );
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message.get());
}