[2026] C++ any | Type Erasing Guide

[2026] C++ any | Type Erasing Guide

이 글의 핵심

A guide that summarizes std::any and variant·void* comparison, type safety, any_cast, practical examples, and performance overhead.

What is any?

std::any is a type erasure container introduced in C++17. You can store values ​​of any type, and you can check the type and extract the value at runtime. A type-safe alternative to void*. 아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
// 실행 예제
std::any a = 42;           // int
a = 3.14;                  // double
a = std::string{"hello"};  // string
// Access after checking the type
if (a.type() == typeid(std::string)) {
    std::cout << std::any_cast<std::string>(a) << std::endl;
}

Why do you need it?:

  • Type Flexibility: When the type is unknown at compile time.
  • Type safety: Safer type erasure than void*.
  • Automatic Management: Automatic life cycle management
  • Exception safety: Exception when accessing the wrong type. 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ void*: type unsafe, manual management
void* ptr = new int(42);
int x = *static_cast<int*>(ptr);  // No type checking
delete ptr;  // manual deletion
// ✅ std::any: type safe, automatically managed
std::any a = 42;
if (a.type() == typeid(int)) {
    int x = std::any_cast<int>(a);  // Check type
}
// automatic destruction

How ​​any works: std::any stores type information and value together internally. Small objects are stored on the stack and large objects are stored on the heap (Small Object Optimization). 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Conceptual Implementation
// 타입 정의
class any {
    void* data_;
    const std::type_info* type_;
    void (*deleter_)(void*);
    
public:
    template<typename T>
    any(T value) {
        data_ = new T(std::move(value));
        type_ = &typeid(T);
        deleter_ = [](void* p) { delete static_cast<T*>(p); };
    }
    
    ~any() {
        if (data_) {
            deleter_(data_);
        }
    }
    
    const std::type_info& type() const {
        return *type_;
    }
};

any vs variant vs optional:

Featuresstd::anystd::variantstd::optional
Storage typeAll typesfixed typeSingle type
type trackingruntimecompile timeN/A
memoryheap (large object)stackstack
PerformanceslowFastVery fast
Type Saferuntimecompile timecompile time
UsePlugins, Settingsstate machine, errornull alternative
아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// any: Any type possible
std::any a = 42;
a = std::string{"hello"};
a = std::vector<int>{1, 2, 3};
// variant: only certain types
std::variant<int, std::string> v = 42;
v = std::string{"hello"};
// v = std::vector<int>{};  // error
// optional: single type
std::optional<int> opt = 42;
opt = std::nullopt;

any vs variant vs void* In-depth

void*

  • Advantages: Any address can be stored, and integration with the C API seems simple.
  • Disadvantage: Ownership/Lifetime/Actual Type depends only on code contract. An invalid static_cast is undefined behavior, and unpaired delete is immediately UB.
  • Summary: Although still used for opaque buffers with fixed layouts (e.g. FFI buffers), std::any or smart pointer+interfaces are safe for holding owned objects. std::variant
  • If a closed set (e.g. int | double | string) is fixed at compile time, visit·holds_alternative will allow most mistakes to be caught at the compile stage.
  • Memory is usually placed in fixed size buffers and often operates without heap allocation (implementation dependent). std::any
  • Suitable when a set of storage types is open and unknown types can enter, such as plugins.
  • In return, type checking goes to runtime, and bad any_cast is an exception (bad_any_cast). Selection guide one line: variant if the type candidate is known, void* (or uintptr_t) if only a heap pointer is passed and the contract is clear, and any if “value ownership + arbitrary type” in between.

Type safety

The safety of std::any is less about “Does it prevent bad casts?” and more about “Can bad casts be detected at runtime?”

  • any_cast<T>(a) must have an internal storage type that exactly matches T (there are reference·cv qualifier rules). If you try to insert int and take it out with long, it will fail.
  • type() returns type_info, so the if (a.type() == typeid(Foo)) pattern is possible, but maintenance is better left to any_cast at once.
  • At interface boundaries, it is safe to limit types to variant or dedicated base classes whenever possible, and to keep any only in layers that really need heterogeneity.

any_cast Completely organized

Copy by value

std::any a = std::string{"hi"};
std::string s = std::any_cast<std::string>(a);  // copy

Edited with reference

std::any a = 10;
std::any_cast<int&>(a) = 20;

Pointer overload (nullptr on failure) 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 실행 예제
std::any a = 3.14;
if (double* p = std::any_cast<double>(&a)) {
    *p = 2.71;
}

any_cast throws bad_any_cast on value/reference overloads if the stored type and the requested type do not match. Pointer overloads return nullptr instead of an exception, so the hot path has the option of branching to pointer form. Move-only type Types that cannot be copied or can only be moved are entered with std::make_any<T>(...), and when taken out, use the std::any_cast<T&>(a) or any_cast<T>(std::move(a)) pattern according to the type.

Performance overhead

Approximate cost factors are as follows:

  1. Type information: type_info search, internal comparison when any_cast.
  2. Storage method: Depending on the implementation, small objects are placed in an inline buffer with Small Object Optimization, but large objects or complex types may be subject to heap allocation.
  3. Copy/Move: If you frequently copy any itself, the cost of copying the stored value is also incurred. mitigation
  • If the candidate type is determined, move it to std::variant.
  • If you repeatedly cast from the same scope, either cast only once and get a reference, or get it as a variant/concrete type in the first place.
  • In configuration maps, etc., check whether a strongly typed struct or toml/json parser result type is better than a string key + any value.

Summary of practical use

SituationWhy any is suitable
Script/plugin passes arbitrary type payloadDifficult to enumerate types in advance
Configuration file values ​​are mixed, such as int/string/bool, etc.Convenient as a simple key-value store (however, if the schema grows, consider a dedicated type)
Various payloads on event busBranch to any_cast in handler
Test mock object/mock dependencyUsed only to a limited extent
Conversely, if the type is fixed, such as numerical hot loop, real-time audio sample processing, or internal API, it is better to avoid any and use variant or a direct type.

Default use

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
// generation
std::any a1 = 42;
std::any a2 = std::string{"hello"};
std::any a3;  // empty any
// check
if (a1.has_value()) {
std::cout << "has value" << std::endl;
}
// type
std::cout << a1.type().name() << std::endl;

Practical example

Example 1: Heterogeneous containers

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
#include <vector>
#include <string>
int main() {
    std::vector<std::any> data;
    
    data.push_back(42);
    data.push_back(3.14);
    data.push_back(std::string{"hello"});
    
    for (const auto& item : data) {
        if (item.type() == typeid(int)) {
            std::cout << "int: " << std::any_cast<int>(item) << std::endl;
        } else if (item.type() == typeid(double)) {
            std::cout << "double: " << std::any_cast<double>(item) << std::endl;
        } else if (item.type() == typeid(std::string)) {
            std::cout << "string: " << std::any_cast<std::string>(item) << std::endl;
        }
    }
}

Example 2: Configuration Repository

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
#include <map>
#include <string>
class Config {
    std::map<std::string, std::any> settings;
    
public:
    template<typename T>
    void set(const std::string& key, const T& value) {
        settings[key] = value;
    }
    
    template<typename T>
    T get(const std::string& key) const {
        auto it = settings.find(key);
        if (it != settings.end()) {
            return std::any_cast<T>(it->second);
        }
throw std::runtime_error("No key");
    }
};
int main() {
    Config config;
    
    config.set("port", 8080);
    config.set("host", std::string{"localhost"});
    config.set("timeout", 30.0);
    
    int port = config.get<int>("port");
    std::string host = config.get<std::string>("host");
    double timeout = config.get<double>("timeout");
}

Example 3: Event system

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
#include <functional>
#include <map>
#include <string>
class EventBus {
    std::map<std::string, std::vector<std::function<void(std::any)>>> handlers;
    
public:
    void on(const std::string& event, std::function<void(std::any)> handler) {
        handlers[event].push_back(handler);
    }
    
    void emit(const std::string& event, std::any data) {
        if (auto it = handlers.find(event); it != handlers.end()) {
            for (auto& handler : it->second) {
                handler(data);
            }
        }
    }
};
int main() {
    EventBus bus;
    
    bus.on("message", [](std::any data) {
        auto msg = std::any_cast<std::string>(data);
std::cout << "Message: " << msg << std::endl;
    });
    
    bus.on("count", [](std::any data) {
        auto count = std::any_cast<int>(data);
std::cout << "Count: " << count << std::endl;
    });
    
    bus.emit("message", std::string{"Hello"});
    bus.emit("count", 42);
}

Example 4: Type-safe wrapper

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <any>
class SafeAny {
    std::any data;
    
public:
    template<typename T>
    void set(const T& value) {
        data = value;
    }
    
    template<typename T>
    std::optional<T> get() const {
        try {
            return std::any_cast<T>(data);
        } catch (const std::bad_any_cast&) {
            return std::nullopt;
        }
    }
    
    bool empty() const {
        return !data.has_value();
    }
};
int main() {
    SafeAny sa;
    sa.set(42);
    
    if (auto val = sa.get<int>()) {
std::cout << "Value: " << *val << std::endl;
    }
    
    if (auto val = sa.get<double>()) {
        std::cout << "double" << std::endl;
    } else {
std::cout << "Type mismatch" << std::endl;
    }
}

Accessing value

아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

std::any a = 42;
// any_cast: value
int x = std::any_cast<int>(a);
// any_cast: pointer
if (int* ptr = std::any_cast<int>(&a)) {
    std::cout << *ptr << std::endl;
}
// any_cast: reference
int& ref = std::any_cast<int&>(a);

Frequently occurring problems

Problem 1: Type mismatch

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

std::any a = 42;
// ❌ Wrong type
try {
    double d = std::any_cast<double>(a);  // std::bad_any_cast
} catch (const std::bad_any_cast&) {
std::cout << "Type mismatch" << std::endl;
}
// ✅ Access after confirmation
if (a.type() == typeid(int)) {
    int x = std::any_cast<int>(a);
}

Issue 2: References

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

std::any a = 42;
// ❌ Copy
int x = std::any_cast<int>(a);
// ✅ See also
int& ref = std::any_cast<int&>(a);
ref = 100;
std::cout << std::any_cast<int>(a) << std::endl;  // 100

Issue 3: Performance

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// any has overhead
// - Save type information
// - Dynamic allocation (large objects)
// - Type check
// ✅ Alternative: variant (if type is known)
std::variant<int, double, std::string> v;

Problem 4: Copy

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
};
// ❌ Non-copyable type
// std::any a = NonCopyable{};  // error
// ✅ Move
std::any a = std::make_any<NonCopyable>();

any vs variant

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// any: any type (runtime)
std::any a = 42;
a = std::string{"hello"};
// variant: defined type (compile time)
std::variant<int, std::string> v = 42;
v = std::string{"hello"};
// variant recommended (if type is known)

Practice pattern

Pattern 1: Plugin system

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Plugin {
public:
    virtual ~Plugin() = default;
    virtual std::string getName() const = 0;
    virtual std::any execute(const std::any& input) = 0;
};
class PluginManager {
    std::map<std::string, std::unique_ptr<Plugin>> plugins_;
    
public:
    void registerPlugin(std::unique_ptr<Plugin> plugin) {
        plugins_[plugin->getName()] = std::move(plugin);
    }
    
    std::any execute(const std::string& name, const std::any& input) {
        if (auto it = plugins_.find(name); it != plugins_.end()) {
            return it->second->execute(input);
        }
throw std::runtime_error("No plugin found");
    }
};
// Plugin implementation
class CalculatorPlugin : public Plugin {
public:
    std::string getName() const override {
        return "calculator";
    }
    
    std::any execute(const std::any& input) override {
        auto values = std::any_cast<std::vector<int>>(input);
        int sum = 0;
        for (int v : values) {
            sum += v;
        }
        return sum;
    }
};

Pattern 2: Type-safe message bus

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class MessageBus {
    struct Handler {
        std::function<void(const std::any&)> callback;
        std::type_index expectedType;
    };
    
    std::map<std::string, std::vector<Handler>> handlers_;
    
public:
    template<typename T>
    void subscribe(const std::string& topic, std::function<void(const T&)> callback) {
        handlers_[topic].push_back({
            [callback](const std::any& data) {
                callback(std::any_cast<const T&>(data));
            },
            std::type_index(typeid(T))
        });
    }
    
    template<typename T>
    void publish(const std::string& topic, const T& data) {
        if (auto it = handlers_.find(topic); it != handlers_.end()) {
            for (auto& handler : it->second) {
                if (handler.expectedType == std::type_index(typeid(T))) {
                    try {
                        handler.callback(std::any(data));
                    } catch (const std::bad_any_cast& e) {
std::cerr << "Type mismatch: " << e.what() << '\n';
                    }
                }
            }
        }
    }
};
// use
MessageBus bus;
bus.subscribe<std::string>("log", [](const std::string& msg) {
std::cout << "log: " << msg << '\n';
});
bus.publish("log", std::string{"Hello"});

Pattern 3: Dynamic property system

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Entity {
    std::map<std::string, std::any> properties_;
    
public:
    template<typename T>
    void setProperty(const std::string& name, const T& value) {
        properties_[name] = value;
    }
    
    template<typename T>
    std::optional<T> getProperty(const std::string& name) const {
        auto it = properties_.find(name);
        if (it == properties_.end()) {
            return std::nullopt;
        }
        
        try {
            return std::any_cast<T>(it->second);
        } catch (const std::bad_any_cast&) {
            return std::nullopt;
        }
    }
    
    bool hasProperty(const std::string& name) const {
        return properties_.count(name) > 0;
    }
};
// use
Entity player;
player.setProperty("health", 100);
player.setProperty("name", std::string{"Hero"});
player.setProperty("position", std::vector<double>{10.0, 20.0});
if (auto health = player.getProperty<int>("health")) {
std::cout << "Health: " << *health << '\n';
}

FAQ

Q1: What is any?

A: A type erasure container in C++17 that can store values ​​of any type. Check the type and extract the value at runtime.

std::any a = 42;
a = 3.14;
a = std::string{"hello"};

Q2: Where is any used?

A:

  • Heterogeneous containers: Store various types in one container
  • Plugin system: Passing data between plugins
  • Settings Storage: Stores various types of setting values
  • Event System: Various types of event data 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
std::vector<std::any> data;
data.push_back(42);
data.push_back(3.14);
data.push_back(std::string{"hello"});

Q3: How do I access the values?

A: Use std::any_cast. In case of type mismatch, an exception is thrown. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

std::any a = 42;
// Extract value (exceptions possible)
int x = std::any_cast<int>(a);
// Pointer extraction (safe)
if (int* ptr = std::any_cast<int>(&a)) {
    std::cout << *ptr << '\n';
}
// Reference extraction
int& ref = std::any_cast<int&>(a);

Q4: What is the performance of any?

A: There is overhead. There are costs of storing type information, dynamic allocation (large objects), and type checking. 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// any: overhead
std::any a = 42;  // Type information + value storage
// variant: faster (if type is known)
std::variant<int, double, std::string> v = 42;

Recommended: Use variant if type is known in advance

Q5: What is the difference from variant?

A:

  • any: Any type possible, runtime type check, slow
  • variant: only defined types, compile-time type check, fast 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// any: any type
std::any a = 42;
a = std::vector<int>{1, 2, 3};  // OK
// variant: only certain types
std::variant<int, double> v = 42;
// v = std::vector<int>{};  // error

Selection criteria:

  • If the type is known in advance: variant
  • If the type is not known in advance: any

Q6: Can any store a reference?

A: Not possible directly, but you can use std::reference_wrapper. 아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

int x = 42;
// ❌ Cannot save reference
// std::any a{x};  // copied
// ✅ Use reference_wrapper
std::any a = std::ref(x);
std::reference_wrapper<int> ref = std::any_cast<std::reference_wrapper<int>>(a);
ref.get() = 100;
std::cout << x << '\n';  // 100

Q7: What is the memory allocation for any?

A: Uses Small Object Optimization (SOO). Small objects are stored on the stack and large objects are stored on the heap. 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Small objects: stack (usually no more than 16-32 bytes)
std::any a1 = 42;  // stack
// Large objects: heap
std::any a2 = std::vector<int>(1000);  // hip

Q8: Any learning resources?

A:


Good article to read together (internal link)

Here’s another article related to this topic.

Practical tips

These are tips that can be applied right away in practice.

Debugging tips

  • If you run into a problem, check the compiler warnings first.
  • Reproduce the problem with a simple test case

Performance Tips

  • Don’t optimize without profiling
  • Set measurable indicators first

Code review tips

  • Check in advance for areas that are frequently pointed out in code reviews.
  • Follow your team’s coding conventions

Practical checklist

This is what you need to check when applying this concept in practice.

Before writing code

  • Is this technique the best way to solve the current problem?
  • Can team members understand and maintain this code?
  • Does it meet the performance requirements?

Writing code

  • Have you resolved all compiler warnings?
  • Have you considered edge cases?
  • Is error handling appropriate?

When reviewing code

  • Is the intent of the code clear?
  • Are there enough test cases?
  • Is it documented? Use this checklist to reduce mistakes and improve code quality.

Keywords covered in this article (related search terms)

This article will be helpful if you search for C++, any, type-erasure, runtime, C++17, etc.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3