【问题标题】:When should I use std::any我什么时候应该使用 std::any
【发布时间】:2018-10-09 07:10:27
【问题描述】:

自 C++17 std::any 引入。现在可以编写这样的代码

#include <iostream>
#include <any>
#include <string>

int main () {
    const double d = 1.2;
    std::any var = d;
    const std::string str = "Hello World";
    var = str;
}

给变量var 分配了一个double,然后给它分配了std::string

为什么要引入std::any

我认为这违反了least astonishment rule,因为我很难想到一种情况,可以用它来更清楚地表达我喜欢表达的东西。

std::any 是有益的时候,谁能给我一个很好的例子。

https://gcc.godbolt.org/z/-kepOD

【问题讨论】:

标签: c++ c++17 stdany


【解决方案1】:

何时使用
void* 作为一种极其不安全的模式,有一些有限的用例,std::any 增加了类型安全,这就是它有一些实际用例的原因。

一些可能性:

  • 在库中 - 当库类型必须在不知道 一组可用类型。
  • 解析文件 - 如果您确实无法指定支持的内容 类型。
  • 消息传递。
  • 与脚本语言绑定。
  • 为脚本语言实现解释器
  • 用户界面 - 控件可以容纳任何东西
  • 编辑器中的实体
    (ref)

【讨论】:

  • "如果您确实无法指定支持的类​​型。"意味着将“任何东西”读入某种存储。好的。以及如何使用这些数据?如果我不知道 std::any 中包含什么,我就不知道如何处理这些数据。如果我知道如何处理数据,我就会知道潜在存储类型的列表。这就是 std::variant 的目的。我不明白这一点......你能解释一下吗?
  • @Klaus 如果解析代码是通用的并且可能不知道所有类型作为编译时间(支持的类型是动态的或者您不想创建依赖项)。
  • 为脚本语言实现解释器”就是一个很好的例子。特别是与“与脚本语言的绑定”结合使用。我发现在实现受约束的 Python 解释器时使用 std::any 非常方便。
  • @Klaus:用例是一些代码知道要存储的类型,但该代码不负责生命周期(包括,也许,制作副本)该数据。
【解决方案2】:

我将其总结为经典的“无法避免时使用”。

我只能想到动态类型的脚本语言的非性能关键实现来表示来自脚本世界的变量,但即使是这样(Boost.Spirit/example/qi/compiler_tutorial 没有它,对于解析器和运行时)。

对于从解析器(例如 Boost.Spirit.X3)到库 API(例如 ASIO)的所有其他内容,通常会有更快/更好/更具体的替代方案,因为很少有东西是真正的“任何东西”,大多数都是更多比这更具体。

  • std::variant 和/或 std::optional 表示“几乎任何价值”
  • std::packaged_task / std::function + lambdas 用于“带参数的回调”,这将是 C API 中 void* 的一种情况。

具体来说,我不会盲目地将它作为void* 的替代品,因为它在堆上的may allocate 内存,这对于高性能代码来说可能是致命的。

【讨论】:

  • 五月,但不是必须的。该规范允许小对象优化 IUC。
  • @StoryTeller - 我更新了答案,并提供了一个指向该问题的链接。每个编译器的细节都有一个答案。请看一看。
【解决方案3】:

std::any 是一个词汇表 类型。当您需要存储任何东西时,您可以使用它作为值。

它有许多“一级”用途:

  1. 在与本身具有此类类型的脚本语言交互时,这是很自然的选择。

  2. 当您拥有一个内容高度多态的属性树,并且树的结构与树的生产者和消费者解耦时。

  3. 当替换相当于 void* 的数据块通过中间层传递时,该中间层实际上并不关心它所携带的内容。

它也可以在其他情况下用作构建块。例如,std::function 可以选择将其值存储在std::any

template<class R, class...Args>
struct func<R(Args...)> {
  mutable std::any state;
  R(*f)(std::any& state, Args&&...) = nullptr;
  template<class T>
  void bind(T&& t) {
    state = std::forward<T>(t);
    f = [](std::any& state, Args&&...args)->R {
      return std::any_cast<T&>(state)(std::forward<Args>(args)...);
    };
  }
  R operator()(Args...args)const {
    return f(state, std::forward<Args>(args)...);
  }
};

这是(大部分)std::function 的一个非常小的实现。基本上我已经使用any 输入擦除复制/移动/销毁。

您可以在其他地方使用它来解决类似的问题(您正在键入擦除某些操作并且还想键入擦除复制/移动/销毁),或generalize it

【讨论】:

  • tbh,这种类型不应该被称为std::any。这是..std::some(thing?)
  • @swift 你必须接受它......好吧,没人,因为它没有改变。但绝对不是我。
【解决方案4】:

Wt中使用,为tabular data提供非模板接口。

内置和 Wt 类型可以转换为字符串,您可以通过专门化 Wt::any_traits 来注册其他转换。这允许将任何内容显示为表格中的条目,视图类不必知道它们所显示的类型。

【讨论】:

  • 您能否发布一个指向使用这些特征的代码示例的链接?
  • 我手头没有例子。您可以查看 Wt 源代码,了解它如何处理 WDateTime
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
相关资源
最近更新 更多