【问题标题】:C++ constructor with optional parameters带有可选参数的 C++ 构造函数
【发布时间】:2020-11-20 06:30:25
【问题描述】:

您好,我是 C++ 新手,我正在尝试创建一个类层次结构,其中每个类代表模式文档中的一个节点,想想 json-schema。看看例如的表示stringstring 可以有三个可选的约束

  • min_length
  • max_length
  • pattern

此外,stringtype,因此有一个基类表示 type,所有类型(booleannumber...)都从该基类继承。现在,实现这一目标的一种方法是编写类似

struct Type {
  const std::string m_name;

  Type(const std::string& name)
  : m_name{name}
  {}

  virtual X Serialize() const {
    //...
  }
};

struct String : Type {
  const int m_max_len;
  const int m_min_len;
  const std::string m_pattern;

  String(int min_len, int max_len, const std::string& pattern)
  : Type("string")
  , m_min_len(min_len)
  , m_max_len(max_len)
  , m_pattern(pattern)
  {}

  X Serialize() const override {
    // If min_length was not set then it should be omitted from the serialized output.
  }
};

String 实现不会使约束成为可选。怎么办?

选项:

  • 可以采用一种策略,将默认构造函数参数设置为一些“非法”值,例如INT_MIN(在这种情况下可以使用,因为长度不能为负数),但在一般情况下不起作用。很可能所有可能的整数都是合法值,pattern 参数也是如此。
  • 您不希望为可选参数的每个可能排列使用不同的构造函数。在这种情况下,有三个可选值会产生 2^3 不同的构造函数。此外,编译器也无法区分构造函数 String(int min_length)String(int max_length)
  • 可以做类似的事情

    String(int* min_length = nullptr, int* max_length = nullptr, const std::string* nullptr)

    但是您必须使用new/delete 或为设置的参数提供左值。
  • 最后每个成员都可以是std::unique_ptr

    String(std::unique_ptr<int> min_value nullptr, std::unique_ptr<int> max_value = nullptr, std::unique_ptr<const std::string> pattern = nullptr)

    但是在创建String 的实例时,您最终会遇到相当复杂的调用。此外,如果实现一个 Types 的容器,它可能有自己的可选参数,很快就会失控。

最后,代码必须兼容 C++14。

【问题讨论】:

  • 我不确定你的问题到底是什么。
  • 我觉得这里不用多态会容易很多
  • “因为长度不能为负” -- 当一个值不能为负时,你应该使用unsigned整数,而不是普通的int。跨度>
  • 我认为你应该为每个参数创建一个类,所以你的调用看起来像String(MinLength::default(),MaxLength(42),string)。您也可以在 blogthis 中找到灵感。
  • 为什么Type 与您的问题相关?当我跳过您的问题中涉及您的类型层次结构的部分时,这与我阅读整个内容时一样有意义。似乎这个问题应该集中在您的带有可选参数的类上,而不是多态性。我在多态性讨论中看到的一个相关细节是提到您需要区分“未给出参数”和“给定参数不施加任何限制”(后者的一个示例是将最小长度设置为 0)。您可能想在文中提及该细节。

标签: c++ class c++14 optional-parameters


【解决方案1】:

你可以简单地使用std::optional:

String(const std::optional<int> &min_len, const std::optional<int> &max_len, 
       const std::optional<std::string> &pattern);

Type *type = new String(5, {}, std::nullptr); // last 2 parameters are omitted.

对于 C++14,您可以使用其他开源库中存在的类似结构(例如 boost::optionalfolly::Optional)。

【讨论】:

  • OP 说“必须兼容 C++14”,std::optional 是 C++17。
  • 我认为通过 const 引用传递 std::optional 没有什么好处。用户可能不会提供std::optional 类型的参数,并且会创建临时变量。这实际上与按值传递相同。但是,您可能希望通过 const 引用(而不是围绕其副本构建的可选)传递字符串参数。在这种情况下,您需要std::optional&lt;std::reference_wrapper&lt;const std::string&gt;&gt; pattern。然后用户可以通过std::nullopt
【解决方案2】:

我缺乏对 @Kostas 的评论的声誉点

为了与 C++14 兼容,您可以尝试具有可选的实验性命名空间(如果它可用/适用于您的编译器)

#include <experimental/optional>

那么你可以使用

String(std::experimental::optional<int> &min_len,.....)
min_len.value_or(-1)

【讨论】:

  • 我喜欢这个想法(赞成),但它似乎不适用于 Visual Studio 2019 和 C++ 2014 :-( 不过我希望它对其他人有所帮助
【解决方案3】:

如果你不能使用std::optional,你可以编写你自己的可以包含或不包含值的类。它不像很多代码。可以让它的接口像 std::optioal 有或者可以做出不同的东西,重要的是数据:

class OptInt {
    bool set_;
    int value_;
public:
    OptInt() : set_(false) , value_(0) {}
    OptInt(int v) : set_(true), value_(v) {}
    // can add other ways how to create it

    bool isSet() const {return set_;}
    int get() const {if (!set_) throw 666; return value_;}
    // can add other operations you want to it.
};

然后你可以使用这种默认构造的 OptInt 作为你的默认参数,所以它不会被设置,但如果调用者提供 int 参数,那么它会被设置。

【讨论】:

  • 即使没有设置/提供最小版本也能创造价值,改进版本(st std one)不会。
  • @Jarod42 那些说“我是 C++ 新手”的人也许不应该混淆需要获得一点性能的位置新闻。如果需要,可以稍后添加。
  • 我没有要求提供更好(和更复杂)的实现,只是告知如果需要可以改进它。(例如,OP 可能具有非默认可构造的类型)。
  • 这暗示了@generic_opto_guy 在原帖中给出的答案。我想我会尝试这种方法。谢谢!
【解决方案4】:

您是否尝试过使用std::optional(C++17 起)? 我知道您提到需要使用 C++14 兼容代码,但有一个 boost::optional 可用。

【讨论】:

    猜你喜欢
    • 2016-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-31
    • 2019-02-26
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多