๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด/C++ ์‘์šฉ

[C++] std::optional์˜ ๋ชจ๋“  ๊ฒƒ

by ์„œ์•„๋ž‘๐Ÿ˜ 2024. 8. 22.

 


std::optional์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…๊ณผ ์‹ค๋ฌด ์˜ˆ์ œ

std::optional์€ C++17์—์„œ ๋„์ž…๋œ ๋งค์šฐ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์œผ๋กœ, ๊ฐ’์ด "์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์—†์„ ์ˆ˜๋„ ์žˆ๋Š”" ์ƒํ™ฉ์„ ์•ˆ์ „ํ•˜๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ด์ „์— nullptr, NULL, ํ˜น์€ ํŠน๋ณ„ํ•œ ๊ฐ’(์˜ˆ: -1)์„ ์‚ฌ์šฉํ•˜์—ฌ "๊ฐ’ ์—†์Œ"์„ ํ‘œํ˜„ํ•˜๋˜ ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ๋” ์•ˆ์ „ํ•˜๊ณ  ๋ช…ํ™•ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

1. std::optional์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

std::optional์€ ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค์ด๋ฉฐ, ์–ด๋–ค ํƒ€์ž… T์— ๋Œ€ํ•ด std::optional<T>๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ํƒ€์ž…์€ T ํƒ€์ž…์˜ ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜๋„ ์žˆ๊ณ , ๊ฐ’์ด ์—†์Œ์„ ๋‚˜ํƒ€๋‚ด๋Š” std::nullopt ์ƒํƒœ์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„ ์–ธ ๋ฐ ์ดˆ๊ธฐํ™”:
std::optional<int> maybeInt;
std::optional<std::string> maybeString = "Hello";
std::optional<double> maybeDouble = std::nullopt; // ๊ฐ’์ด ์—†์Œ

  • ๊ฐ’ ํ• ๋‹น ๋ฐ ์ ‘๊ทผ:
maybeInt = 42;
if (maybeInt) {
    std::cout << "Value: " << *maybeInt << std::endl; // Dereferencing the optional
} else {
    std::cout << "No value" << std::endl;
}


2. ์‹ค๋ฌด์—์„œ์˜ std::optional ์‚ฌ์šฉ ์˜ˆ์ œ

์˜ˆ์ œ 1: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ์—์„œ ์‚ฌ์šฉ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ์—์„œ ํŠน์ • ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์„ ์ฐพ์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ std::optional์„ ์‚ฌ์šฉํ•˜์—ฌ ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#include <iostream>
#include <optional>
#include <unordered_map>
#include <string>

class Database {
public:
    std::optional<std::string> getUserById(int id) {
        if (auto it = users.find(id); it != users.end()) {
            return it->second;
        }
        return std::nullopt; // ์‚ฌ์šฉ์ž ์—†์Œ
    }

    void addUser(int id, const std::string& name) {
        users[id] = name;
    }

private:
    std::unordered_map<int, std::string> users;
};

int main() {
    Database db;
    db.addUser(1, "Alice");
    db.addUser(2, "Bob");

    auto user = db.getUserById(1);
    if (user) {
        std::cout << "User found: " << *user << std::endl;
    } else {
        std::cout << "User not found" << std::endl;
    }

    user = db.getUserById(3);
    if (user) {
        std::cout << "User found: " << *user << std::endl;
    } else {
        std::cout << "User not found" << std::endl;
    }

    return 0;
}

์„ค๋ช…: ์ด ์ฝ”๋“œ์—์„œ getUserById ํ•จ์ˆ˜๋Š” ํŠน์ • ์‚ฌ์šฉ์ž ID๋ฅผ ์กฐํšŒํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์กด์žฌํ•˜๋ฉด std::optional<std::string>์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด std::nullopt์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ธก์—์„œ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ 2: ์„ค์ • ํŒŒ์ผ์—์„œ ์˜ต์…˜ ๊ฐ’ ์ฝ๊ธฐ

์–ด๋–ค ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์„ค์ • ํŒŒ์ผ์—์„œ ํŠน์ • ์˜ต์…˜ ๊ฐ’์„ ์ฝ์–ด์™€์•ผ ํ•˜์ง€๋งŒ, ํ•ด๋‹น ์˜ต์…˜์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ std::optional์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#include <iostream>
#include <optional>
#include <map>
#include <string>

class ConfigReader {
public:
    ConfigReader(const std::map<std::string, std::string>& configMap)
        : config(configMap) {}

    std::optional<std::string> getValue(const std::string& key) const {
        auto it = config.find(key);
        if (it != config.end()) {
            return it->second;
        }
        return std::nullopt;
    }

private:
    std::map<std::string, std::string> config;
};

int main() {
    std::map<std::string, std::string> configMap = {
        {"host", "localhost"},
        {"port", "8080"}
    };
    ConfigReader config(configMap);

    auto host = config.getValue("host");
    std::cout << "Host: " << (host ? *host : "Not specified") << std::endl;

    auto timeout = config.getValue("timeout");
    std::cout << "Timeout: " << (timeout ? *timeout : "Not specified") << std::endl;

    return 0;
}

์„ค๋ช…: ์ด ์˜ˆ์ œ์—์„œ๋Š” ConfigReader ํด๋ž˜์Šค๊ฐ€ ์„ค์ • ํŒŒ์ผ์„ ์ฝ์–ด์™€ ํŠน์ • ํ‚ค์— ๋Œ€ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด std::nullopt์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ, ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ธก์—์„œ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ 3: ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์—์„œ ์‘๋‹ต ์ฒ˜๋ฆฌ

๋„คํŠธ์›Œํฌ ํ†ต์‹ ์—์„œ ์„œ๋ฒ„์˜ ์‘๋‹ต์ด ์˜ฌ ์ˆ˜๋„ ์žˆ๊ณ , ํƒ€์ž„์•„์›ƒ์œผ๋กœ ์ธํ•ด ์‘๋‹ต์ด ์˜ค์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ std::optional์„ ์‚ฌ์šฉํ•˜์—ฌ ์‘๋‹ต์ด ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#include <iostream>
#include <optional>
#include <string>
#include <thread>
#include <chrono>

std::optional<std::string> fetchDataFromServer(bool simulateTimeout) {
    if (simulateTimeout) {
        return std::nullopt; // ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ
    } else {
        std::this_thread::sleep_for(std::chrono::seconds(1)); // ๋„คํŠธ์›Œํฌ ์ง€์—ฐ ์‹œ๊ฐ„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        return "Server response data";
    }
}

int main() {
    auto response = fetchDataFromServer(false);
    if (response) {
        std::cout << "Received: " << *response << std::endl;
    } else {
        std::cout << "No response from server (timeout)." << std::endl;
    }

    response = fetchDataFromServer(true);
    if (response) {
        std::cout << "Received: " << *response << std::endl;
    } else {
        std::cout << "No response from server (timeout)." << std::endl;
    }

    return 0;
}

์„ค๋ช…: ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” fetchDataFromServer ํ•จ์ˆ˜๋Š” ๋„คํŠธ์›Œํฌ ํƒ€์ž„์•„์›ƒ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜์—ฌ std::optional<std::string>์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ•˜๋ฉด std::nullopt์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์‘๋‹ต์ด ์—†์Œ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

3. std::optional ์‚ฌ์šฉ ์‹œ์˜ ์ฃผ์˜ ์‚ฌํ•ญ

  • ์˜ต์…”๋„์˜ ํฌ๊ธฐ: std::optional์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฐ’๊ณผ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ’ ํƒ€์ž…์˜ ํฌ๊ธฐ๋ณด๋‹ค ๋” ํฐ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์€ ํฌ๊ธฐ์˜ ํƒ€์ž…์— ๋Œ€ํ•ด์„œ๋Š” ํฌ๊ฒŒ ๋ฌธ์ œ๋˜์ง€ ์•Š์ง€๋งŒ, ํฐ ๊ฐ์ฒด๋ฅผ ์˜ต์…”๋„๋กœ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์— ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋นˆ ์ƒํƒœ ์ ‘๊ทผ: std::optional์ด ๋น„์–ด์žˆ๋Š” ์ƒํƒœ์—์„œ ๊ฐ’์„ ์ฐธ์กฐํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ญ์ƒ if (opt) ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด ๊ฐ’์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ํ›„์— ์ ‘๊ทผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ๋ณธ ์ œ๊ณต ํ•จ์ˆ˜ ํ™œ์šฉ: std::optional์€ value_or, has_value, emplace ๋“ฑ์˜ ์œ ์šฉํ•œ ๋ฉค๋ฒ„ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ ๊ฐ’์„ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๊ฐ’์„ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
std::optional<int> optInt = std::nullopt;
int value = optInt.value_or(10); // optInt๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด 10์„ ์‚ฌ์šฉ

std::optional์€ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ๊ฐ€๋…์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋กœ, ํŠนํžˆ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์œผ๋กœ ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด "๊ฐ’ ์—†์Œ"์„ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

 

๋Œ“๊ธ€