[2026] C++ std::any vs void* Complete Comparison | Type-Safe vs Unsafe Type Erasure
이 글의 핵심
Master C++ type erasure: std::any (type-safe, runtime checks) vs void* (unsafe, manual casting). Complete comparison with use cases, performance, and when to choose each.
Overview
Both std::any and void* provide type erasure—storing values of any type—but with vastly different safety guarantees.
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// std::any (C++17, type-safe)
std::any value = 42;
int x = std::any_cast<int>(value); // Runtime type check
// void* (C, unsafe)
void* ptr = new int(42);
int y = *(int*)ptr; // No type check, dangerous!
Key difference: std::any remembers the stored type and checks it at runtime; void* forgets the type entirely.
std::any
Basic Usage
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <any>
#include <iostream>
int main() {
std::any value;
// Store int
value = 42;
cout << std::any_cast<int>(value) << endl; // 42
// Store string
value = std::string("hello");
cout << std::any_cast<std::string>(value) << endl; // hello
// Store custom type
struct Point { int x, y; };
value = Point{10, 20};
auto p = std::any_cast<Point>(value);
cout << p.x << ", " << p.y << endl; // 10, 20
}
Output:
42
hello
10, 20
Type Checking
다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::any value = 42;
// Check type
if (value.type() == typeid(int)) {
cout << "It's an int" << endl;
}
// has_value()
if (value.has_value()) {
cout << "Has value" << endl;
}
// Wrong type cast → exception
try {
auto s = std::any_cast<std::string>(value); // Throws!
} catch (const std::bad_any_cast& e) {
cerr << "Bad cast: " << e.what() << endl;
}
Output:
It's an int
Has value
Bad cast: bad any_cast
Safe Casting
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::any value = 42;
// Pointer cast (returns nullptr on failure)
int* ptr = std::any_cast<int>(&value);
if (ptr) {
cout << *ptr << endl; // 42
}
// Wrong type → nullptr
std::string* strPtr = std::any_cast<std::string>(&value);
if (!strPtr) {
cout << "Not a string" << endl;
}
Output:
42
Not a string
void*
Basic Usage
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
void* ptr = new int(42);
// Cast back to int*
int* intPtr = (int*)ptr;
cout << *intPtr << endl; // 42
delete intPtr;
Warning: No type safety—you must remember the original type.
Dangerous Scenarios
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Wrong type cast (undefined behavior)
void* ptr = new int(42);
double* dblPtr = (double*)ptr; // Wrong type!
cout << *dblPtr << endl; // Garbage or crash
// ❌ Forgot to delete (memory leak)
void* ptr = new int(42);
// ....ptr goes out of scope, never deleted
// ❌ Double delete
void* ptr = new int(42);
delete (int*)ptr;
delete (int*)ptr; // Crash!
C API Example
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// C API using void*
void processData(void* userData, void (*callback)(void*)) {
// ....processing ...
callback(userData);
}
// Usage
struct Context {
int id;
std::string name;
};
void myCallback(void* ptr) {
Context* ctx = (Context*)ptr; // Manual cast
cout << ctx->id << ", " << ctx->name << endl;
}
int main() {
Context ctx{1, "Alice"};
processData(&ctx, myCallback);
}
Output:
1, Alice
Comparison
Feature Comparison
| Feature | std::any | void* |
|---|---|---|
| Type safety | ✅ Runtime checks | ❌ No checks |
| Exception on wrong cast | ✅ Yes | ❌ No (UB) |
| Type info stored | ✅ Yes | ❌ No |
| Memory management | ✅ Automatic | ❌ Manual |
| Small object optimization | ✅ Yes | ❌ No |
| C compatibility | ❌ No | ✅ Yes |
| Performance | Good | Slightly faster |
Safety Comparison
아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// std::any: Safe
std::any value = 42;
try {
auto s = std::any_cast<std::string>(value); // Throws bad_any_cast
} catch (const std::bad_any_cast&) {
cout << "Caught wrong type" << endl;
}
// void*: Unsafe
void* ptr = new int(42);
std::string* s = (std::string*)ptr; // Compiles, but UB!
cout << *s << endl; // Crash or garbage
Performance Comparison
// Benchmark: 1,000,000 operations
// std::any: 15ms (type info overhead)
// void*: 10ms (no overhead)
Key: void* is slightly faster, but the difference is negligible in most applications.
When to Use Each
Use std::any When:
- ✅ Type safety is important
- ✅ Storing heterogeneous data in containers
- ✅ Building modern C++ APIs
- ✅ Runtime type checks needed 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Event system
struct Event {
std::string type;
std::any data;
};
void handleEvent(const Event& e) {
if (e.type == "click") {
auto pos = std::any_cast<Point>(e.data);
// ...
}
}
Use void* When:
- ✅ Interfacing with C APIs
- ✅ Legacy code maintenance
- ✅ Extreme performance requirements
- ✅ Custom memory management 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// C API callback
void setCallback(void* userData, void (*callback)(void*)) {
// ...
}
Common Mistakes
Mistake 1: Forgetting Type with void*
다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Lost type information
void* ptr = new std::string("hello");
// ....later ...
int* intPtr = (int*)ptr; // Wrong type! UB
Fix: Use std::any or document type carefully.
Mistake 2: Memory Leak with void*
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Memory leak
void* ptr = new int(42);
// ....forgot to delete
// ✅ Use smart pointer
std::unique_ptr<void, void(*)(void*)> ptr(
new int(42),
[](void* p) { delete (int*)p; }
);
Fix: Use RAII or std::any.
Mistake 3: Wrong any_cast
아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Value cast throws on failure
std::any value = 42;
auto s = std::any_cast<std::string>(value); // Throws!
// ✅ Pointer cast returns nullptr on failure
std::string* s = std::any_cast<std::string>(&value);
if (!s) {
cout << "Not a string" << endl;
}
Practical Examples
Example 1: Heterogeneous Container
다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// std::any
vector<std::any> data;
data.push_back(42);
data.push_back(std::string("hello"));
data.push_back(3.14);
for (auto& item : data) {
if (item.type() == typeid(int)) {
cout << "int: " << std::any_cast<int>(item) << endl;
} else if (item.type() == typeid(std::string)) {
cout << "string: " << std::any_cast<std::string>(item) << endl;
} else if (item.type() == typeid(double)) {
cout << "double: " << std::any_cast<double>(item) << endl;
}
}
Output:
int: 42
string: hello
double: 3.14
Example 2: Plugin System
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Plugin interface
class Plugin {
public:
virtual ~Plugin() = default;
virtual void execute(std::any context) = 0;
};
class LogPlugin : public Plugin {
public:
void execute(std::any context) override {
if (context.type() == typeid(std::string)) {
auto msg = std::any_cast<std::string>(context);
cout << "Log: " << msg << endl;
}
}
};
int main() {
LogPlugin plugin;
plugin.execute(std::string("Hello"));
}
Output:
Log: Hello
Example 3: C API Wrapper
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// C API
extern "C" {
void c_callback(void* userData);
}
// C++ wrapper
class CallbackWrapper {
std::function<void()> func;
public:
CallbackWrapper(std::function<void()> f) : func(f) {}
static void invoke(void* ptr) {
auto* wrapper = (CallbackWrapper*)ptr;
wrapper->func();
}
void* getUserData() {
return this;
}
};
void registerCallback() {
CallbackWrapper wrapper([]() {
cout << "Callback invoked" << endl;
});
c_callback(wrapper.getUserData());
}
Production Patterns
Pattern 1: Event System with std::any
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class EventBus {
unordered_map<string, vector<function<void(std::any)>>> handlers;
public:
void subscribe(const string& eventType, function<void(std::any)> handler) {
handlers[eventType].push_back(handler);
}
void publish(const string& eventType, std::any data) {
for (auto& handler : handlers[eventType]) {
handler(data);
}
}
};
struct ClickEvent { int x, y; };
int main() {
EventBus bus;
bus.subscribe("click", [](std::any data) {
auto evt = std::any_cast<ClickEvent>(data);
cout << "Click at " << evt.x << ", " << evt.y << endl;
});
bus.publish("click", ClickEvent{100, 200});
}
Output:
Click at 100, 200
Pattern 2: Config System
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Config {
unordered_map<string, std::any> values;
public:
template<typename T>
void set(const string& key, const T& value) {
values[key] = value;
}
template<typename T>
T get(const string& key, const T& defaultValue = T{}) {
auto it = values.find(key);
if (it == values.end()) {
return defaultValue;
}
try {
return std::any_cast<T>(it->second);
} catch (const std::bad_any_cast&) {
return defaultValue;
}
}
};
int main() {
Config cfg;
cfg.set("port", 8080);
cfg.set("host", std::string("localhost"));
cfg.set("debug", true);
cout << cfg.get<int>("port") << endl; // 8080
cout << cfg.get<string>("host") << endl; // localhost
cout << cfg.get<bool>("debug") << endl; // 1
cout << cfg.get<int>("missing", 9000) << endl; // 9000 (default)
}
Output: 다음은 간단한 code 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
8080
localhost
1
9000
Summary
Key Points
- std::any: Type-safe, runtime checks, exceptions
- void*: Unsafe, manual casting, no checks
- Use std::any: For modern C++ code
- Use void*: For C API interop, legacy code
- Performance: void* slightly faster, but std::any overhead is minimal
- Safety: std::any prevents undefined behavior
Decision Matrix
| Scenario | Recommendation |
|---|---|
| Modern C++ API | std::any |
| C API interop | void* |
| Type safety critical | std::any |
| Legacy codebase | void* (migrate to std::any) |
| Extreme performance | void* (measure first) |
| Heterogeneous containers | std::any or std::variant |
Migration Path
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Old (void*)
void* data = new int(42);
int value = *(int*)data;
delete (int*)data;
// New (std::any)
std::any data = 42;
int value = std::any_cast<int>(data);
// Automatic cleanup
Related Articles
- C++ std::any Complete Guide
- C++ void* and Type Erasure
- C++ Type Erasure Complete Guide
- C++ std::variant vs union Comparison
Keywords
C++ std::any, void pointer, type erasure, type safety, runtime type checking, std::any_cast, bad_any_cast One-line summary: std::any provides type-safe type erasure with runtime checks, while void* offers unsafe manual casting—prefer std::any for modern C++ code.