1

Let's say, I have an std::any that store type T in it. Is it possible to create another std::any that will contain type std::reference_wrapper<const T>? Like

std::any original = std::string("Test string");
std::any reference;

// Magic here

auto ref = std::any_cast<std::reference_wrapper<const std::string>>(reference); // Works
rustyx
  • 80,671
  • 25
  • 200
  • 267
val - disappointed in SE
  • 1,475
  • 3
  • 16
  • 40

3 Answers3

1

It is impossible, unless you know the exact stored type (see the other answer), or the list of possible types (in which case you can switch over original.type()).

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
1

If you want this badly enough, you can create a little wrapper class around a std::any that captures and type-erases the "convert to reference_wrapper" operation when it is constructed:

class any_refable
{
public:
    std::any ref() const { return converter(any); }

    const std::any& get() const& { return any; }
    const std::any&& get() const&& { return std::move(any); }
    std::any& get() & { return any; }
    std::any&& get() && { return std::move(any); }

    any_refable() = default;

    template <typename T, typename = std::enable_if_t<! std::is_same_v<std::decay_t<T>, any_refable>>>
    any_refable(T&& v) : any(std::forward<T>(v)), converter(make_converter<std::decay_t<T>>()) {}

    template <typename T, typename = std::enable_if_t<! std::is_same_v<std::decay_t<T>, any_refable>>>
    any_refable& operator=(T&& v) { any = std::forward<T>(v); converter = make_converter<std::decay_t<T>>(); return *this; }

private:
    using converter_t = std::any (*)(const std::any&);

    std::any any;
    converter_t converter = nullptr;

    template <typename T>
    static converter_t make_converter() {
        return [](const std::any& any) { return std::any(std::cref(std::any_cast<const T&>(any))); };
    }
};

DEMO

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • Looks interesting. Wouldn't deriving from `std::ANY` provide a bit more intuitive interface tho? – val - disappointed in SE Jan 09 '20 at 11:03
  • There are numerous reasons why I would avoid inheritance here, that could fill a whole other question (that would probably be closed for being too opinion-based). Let me quote from three items of Sutter & Alexandrescu's book _C++ Coding Standards_. Item 34: "Prefer composition to inheritance. Inheritance is the tightest coupling relationship in C++, second only to friendship." – Oktalist Jan 09 '20 at 18:47
  • Item 35: "Avoid inheriting from classes that were not designed to be base classes. Using a standalone class as a base is a serious design error. Avoid inheriting from concrete classes. Inheriting from a class with a public non-virtual destructor risks undefined behavior." Item 37: "Inheritance is substitutability. All base contracts must be fulfilled." [Here](https://godbolt.org/z/_oj3Yp) is an example of how inheritance could make it too easy to invoke undefined behavior. Granted, that's possible with composition too, but harder to do by accident. – Oktalist Jan 09 '20 at 18:49
0

The following works:

#include <iostream>
#include <any>
#include <functional>

int main() {
    std::any original = std::string("Test string");
    std::any ref = std::cref(std::any_cast<const std::string&>(original)); 

    std::any_cast<std::string&>(original)[0] = 'X';

    std::cout << std::any_cast<std::reference_wrapper<const std::string>>(ref).get() << '\n'; // Prints Xest string
}

Edit: As said in the comment, this only works if the type is known at compile time. If the type of the contained object is arbitrary, it is impossible, since the std::reference_wrapper has to be constructed at some point and for this it needs to know the type it has to wrap at compile time, there is no way around this since C++ is statically typed.

n314159
  • 4,990
  • 1
  • 5
  • 20