|
| 1 | +# std::any |
| 2 | +`std::any` is a feature introduced in C++17. It provides a type-safe container for single values of any type. |
| 3 | + |
| 4 | +## Explanation |
| 5 | +- `std::any` can store an instance of any type that satisfies `CopyConstructible`. |
| 6 | +- It's a better, type-safe alternative to `void*` for storing values of unknown type at compile time. |
| 7 | +- `std::any_cast` is a function template in C++ that is used to retrieve the value stored in an `std::any` object. It serves two primary purposes: to safely extract a value of a specific type from an `std::any` object and to check the type of the stored value at runtime. |
| 8 | +- If the cast to the original type fails (i.e., if you try to cast it to a different type), an exception of type `std::bad_any_cast` is thrown. |
| 9 | + |
| 10 | +## Example |
| 11 | + |
| 12 | +```cpp |
| 13 | +#include <iostream> |
| 14 | +#include <any> |
| 15 | +#include <string> |
| 16 | + |
| 17 | +int main() { |
| 18 | + std::any a = 1; // Storing an int |
| 19 | + std::cout << std::any_cast<int>(a) << '\n'; // Casting back to int |
| 20 | + |
| 21 | + a = 3.14; // Now storing a double |
| 22 | + std::cout << std::any_cast<double>(a) << '\n'; // Casting back to double |
| 23 | + |
| 24 | + a = std::string("Hello, std::any!"); |
| 25 | + std::cout << std::any_cast<std::string>(a) << '\n'; // Casting back to std::string |
| 26 | + |
| 27 | + // Attempting to cast to an incorrect type will throw std::bad_any_cast |
| 28 | + try { |
| 29 | + std::cout << std::any_cast<int>(a) << '\n'; |
| 30 | + } catch (const std::bad_any_cast& e) { |
| 31 | + std::cout << e.what() << '\n'; |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +In this example: |
| 37 | +- `std::any a` is first assigned an integer, then a double, and finally a `std::string`. |
| 38 | +- For each assignment, it's cast back to its original type using `std::any_cast`. |
| 39 | +- The last block demonstrates the exception handling in case of a wrong cast. When we try to cast the `std::string` to `int`, `std::bad_any_cast` is thrown. |
| 40 | + |
| 41 | +## std::any with and without satisfying the CopyConstructible condition |
| 42 | + |
| 43 | +To illustrate a non-`CopyConstructible` type, we can create a class that explicitly deletes its copy constructor. This class can't be used with `std::any` directly, as it violates the `CopyConstructible` requirement. |
| 44 | + |
| 45 | +```cpp |
| 46 | +#include <iostream> |
| 47 | +#include <any> |
| 48 | +#include <string> |
| 49 | + |
| 50 | +class NonCopyable { |
| 51 | +public: |
| 52 | + NonCopyable() = default; |
| 53 | + NonCopyable(const NonCopyable&) = delete; // deleting the copy constructor |
| 54 | + NonCopyable& operator=(const NonCopyable&) = delete; |
| 55 | +}; |
| 56 | + |
| 57 | +int main() { |
| 58 | + NonCopyable nonCopyableObj; |
| 59 | + |
| 60 | + // This line will cause a compilation error because NonCopyable is not CopyConstructible |
| 61 | + // std::any a = nonCopyableObj; |
| 62 | + |
| 63 | + // Uncommenting the above line will lead to a compilation error |
| 64 | + // Instead, you can store a pointer to the object |
| 65 | + std::any a = &nonCopyableObj; // Storing a pointer is fine |
| 66 | + |
| 67 | + // Retrieving the pointer back |
| 68 | + auto storedObj = std::any_cast<NonCopyable*>(a); |
| 69 | + // Use storedObj as a pointer to NonCopyable |
| 70 | +} |
| 71 | +``` |
| 72 | +
|
| 73 | +In the second example, attempting to store an instance of `NonCopyable` in `std::any` will result in a compilation error. However, storing a pointer to a `NonCopyable` object works because pointers are `CopyConstructible`. |
| 74 | +
|
| 75 | +
|
| 76 | +## How to find out if a class is CopyConstructible |
| 77 | +
|
| 78 | +### 1. Check the Source Code |
| 79 | +A class is `CopyConstructible` if it has a public copy constructor. Look for something like: |
| 80 | +```cpp |
| 81 | +ClassName(const ClassName&); |
| 82 | +``` |
| 83 | +If this constructor is public and not deleted, the class is `CopyConstructible`. |
| 84 | + |
| 85 | +### 2. Use Type Traits |
| 86 | +C++ provides type traits in the `<type_traits>` header that can be used to check certain properties at compile-time. You can use `std::is_copy_constructible` to check if a type is `CopyConstructible`. For example: |
| 87 | +```cpp |
| 88 | +#include <type_traits> |
| 89 | + |
| 90 | +if constexpr (std::is_copy_constructible<ThirdPartyClass>::value) { |
| 91 | + // The class is CopyConstructible |
| 92 | +} else { |
| 93 | + // The class is not CopyConstructible |
| 94 | +} |
| 95 | +``` |
| 96 | +This method is useful when you want to write code that depends on whether a type is `CopyConstructible` or not. |
| 97 | + |
| 98 | +### 3. Attempt Compilation |
| 99 | +As a quick-and-dirty method, you can write a small piece of code that attempts to copy an instance of the class. If the code fails to compile, it's likely that the class is not `CopyConstructible`. However, this method is less precise and should be used cautiously. |
0 commit comments