【问题标题】:Easy way to create object with members of various datatypes使用各种数据类型的成员创建对象的简单方法
【发布时间】:2019-07-30 14:39:30
【问题描述】:

在 C++ 中,我经常需要使用一个包含各种数据类型的极其简单的原始对象,以便我可以在函数之间轻松地传递它。在 Python 中,我通过使用字典来实现这一点。例如:

easy = {"keyframe_range":[], "value":0.25, "interpolation":"bezier"}

但是,在 C++ 中,如果我想创建类似的东西,我需要:

struct obj
{
  vector<int> keyframe_range;
  float value;
  string interpolation;

  obj(vector<int> vv, float ff, string ss) : keyframe_range(vv), value(ff), interpolation(ss) {}

};

obj easy(vector<int>(), 0.25, "bezier");

当我需要随心所欲地创建谁知道什么样的对象时,为我需要的每个对象手动编写参数化构造函数是非常低效和浪费时间的。在 Python 中,我基本上可以通过字典避免这种情况;但是,C++ 中的 unordered_maps 必须映射到相同的数据类型,所以它们并不是我真正想要的。

本质上,我只是想要一种简单的方法来创建一个简单的对象,该对象充当各种数据类型的项目的集合,仅此而已。这可以在 C++ 11 中实现吗?如果可以,如何实现?

【问题讨论】:

  • 结构/类是要走的路。如果您不喜欢编写构造函数,请让您的 IDE 完成这项工作。
  • 您可以使用模板来实现这一点,variadic templates
  • 还有std::tuple
  • @nada tuple 很酷,但它被设计用于通用编程的情况(例如存储可变参数,所以以后它们可以被std::apply 使用)。我讨厌 tuple 没有模板代码。它使代码更难阅读和维护。

标签: c++ c++11 object constructor initialization


【解决方案1】:

手动编写效率极低且浪费大量时间 我需要的每个对象的参数化构造函数。

我完全同意。但是,没有问题,因为您实际上不需要为每个简单的结构编写构造函数:

#include <vector>
#include <string>
#include <iostream>

struct obj
{
    std::vector<int> keyframe_range;
    float value;
    std::string interpolation;
};

int main() {
    obj easy{ {}, 0.25, "bezier"};
    std::cout << easy.value << " " << easy.interpolation;        
}

它被称为aggregate initialization 并且可以在类有时完成

  • 没有私有或受保护的直接(C++17 起)非静态数据成员

  • 没有用户声明的构造函数(C++11 前)

  • 没有用户提供的构造函数(允许显式默认或删除的构造函数)(C++11 起)(C++17 前)

  • 没有用户提供的、继承的或显式的构造函数(允许显式默认或删除的构造函数)(C++17 起)(直到 C++20)

  • 没有用户声明或继承的构造函数 [...]

还有一些限制,通常都适用于这种简单的结构(它们被称为聚合)。

本质上,我只是想要一种简单的方法来创建一个简单的对象 它充当各种数据类型的项目的集合,什么都没有 更多的。这可以在 C++ 11 中实现吗?如果可以,如何实现?

或者你应该看看std::tuple,虽然编写你自己的类的好处是给成员提供有意义的名字,你可以区分类型,因为std::tuple是完全相同的。

另一方面,std::tuple 的优势在于它带有几个已经定义好的运算符(例如operator&lt;)。因此,根据您的用例,您可以选择其中一种。

【讨论】:

  • C++20 之前的一个缺点是您仍然必须实现比较运算符。一旦我们得到auto operator(type const&amp;)&lt;=&gt; = default;,那就太好了。
  • @NathanOliver,你到底是什么意思?
  • @ereHsaWyhsipS 使用这种使用obj 的方法,您不能拥有两个obj 并像obj1 == obj2 那样比较它们,因为没有为obj 定义operator ==。如果需要,您将不得不编写该代码。在我的回答中,tuple 已经为您准备好了,因此您不必编写任何额外的代码。也就是说,在 C++20 中,我们将获得宇宙飞船运算符 operator &lt;=&gt;,您可以通过添加到 obj auto operator(obj const&amp;)&lt;=&gt; = default; 来默认它让编译器为您构建所有逻辑运算符
【解决方案2】:

您可以为此使用std::tuple。它允许您创建异构类型的对象,而无需指定所有样板。不过,它确实有不利的一面。你不能给元组的成员一个名字。您必须通过它们所在的“索引”(第一个成员是索引 0)或类型(这仅在您不复制类型时才有效)来访问它们。所以

easy = {"keyframe_range":[], "value":0.25, "interpolation":"bezier"}

会变成

auto easy = std::tuple<std::vector<int>, float, std::string>{{}, 0.25, "bezier"}

你可以访问std::get&lt;1&gt;(easy)/std::get&lt;float&gt;(easy) 之类的成员来获取float 成员。


C++17:您可以使用structured binding 来获得对成员的命名访问,例如

auto&[easy_vec, easy_float, easy_string] = easy;

现在easy_veceasy_floateasy_string 是对元组成员的引用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-03
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    相关资源
    最近更新 更多