是否可以仅从 std::any 使用 std::reference_wrapper 创建 std::any?

Posted

技术标签:

【中文标题】是否可以仅从 std::any 使用 std::reference_wrapper 创建 std::any?【英文标题】:Is it possible to create std::any with std::reference_wrapper from just std::any? 【发布时间】:2020-01-08 18:50:07 【问题描述】:

假设,我有一个 std::any,其中存储类型 T。是否可以创建另一个包含std::reference_wrapper<const T> 类型的std::any?喜欢

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

【问题讨论】:

【参考方案1】:

这是不可能的,除非您知道确切的存储类型(请参阅其他答案)或可能类型的列表(在这种情况下,您可以 switch 超过 original.type())。

【讨论】:

【参考方案2】:

如果你非常想要这个,你可以围绕 std::any 创建一个小包装类,它在构造时捕获并类型擦除“转换为reference_wrapper”操作:

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

【讨论】:

看起来很有趣。从std::ANY 派生不会提供更直观的界面吗? 我在这里避免继承的原因有很多,这可能会填补另一个问题(这可能会因为过于基于意见而被关闭)。让我引用 Sutter 和 Alexandrescu 的书 C++ 编码标准 中的三个条目。第 34 条:“更喜欢组合而不是继承。继承是 C++ 中最紧密的耦合关系,仅次于友谊。” 第 35 项:“避免从不是设计为基类的类继承。使用独立类作为基类是严重的设计错误。避免从具体类继承。从具有公共非虚拟析构函数有未定义行为的风险。”第 37 项:“继承是可替代性。必须履行所有基础合同。” Here 是一个示例,说明继承如何使调用未定义的行为变得过于容易。诚然,作曲也可以做到这一点,但更难偶然做到。【参考方案3】:

以下作品:

#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

编辑:正如评论中所说,这仅在编译时已知类型的情况下才有效。如果包含的对象的类型是任意的,这是不可能的,因为std::reference_wrapper 必须在某个时候构造,为此它需要知道它必须在编译时包装的类型,没有办法解决这个问题,因为C++ 是静态类型的。

【讨论】:

这需要你知道确切的存储类型,所以有点作弊。

以上是关于是否可以仅从 std::any 使用 std::reference_wrapper 创建 std::any?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将两个任意函数与 C++17 中的 std::any 进行比较?

带有 std::any 和 std::optional 的 any_cast

为 std::any_of 等提供反向迭代器是不是有意义?

std::any 由 std::exception_ptr

`boost::any` 和 `std::any` 之间的区别

我啥时候应该使用 std::any