【问题标题】:How to provide a default value for the first of a pair?如何为一对中的第一个提供默认值?
【发布时间】:2014-04-02 17:50:20
【问题描述】:

我使用的是pair类型:

typedef std::pair<int, std::string> Nomnom;

它的使用示例:

void DoIt(const Nomnom &toNom) { 
    ...
}
void DoItAgain(const Nomnom &toNomAgain) { 
    ...
}

在我的情况下,如果未指定,则为该对提供默认值是有意义的。也就是说,我希望能够做到这一点:

DoIt("Thisisit");
DoItAgain("Thisisit");

让它等同于这个:

DoIt(NomNom(0, "Thisisit"));
DoItAgain(NomNom(0, "Thisisit"));

我想在每次使用 Nomnom 时不定义两个变体。

除了为 Nomnom 定义一个类而不是 typedef,提供一个单参数构造函数,然后提供调用 std::pair 构造函数的零参数和双参数构造函数之外,有什么方法可以实现这一点?

【问题讨论】:

  • 是的,它被称为重载。您可以同时拥有ClassNoms(Nomnom oneIn)ClassNoms(int key)
  • 取决于您可以应用的int 的合理默认值,不是吗?在这种情况下,您可以使用默认构造函数ClassNoms() : one(std::make_pair(-1,"") 轻松下车。还是我只是错过了您的要求?
  • 抱歉,不清楚。我想要Nomnom 的默认构造函数,而不是ClassNoms 的默认构造函数。请注意,DoIt 采用 Nomnom,而不是 ClassNoms。我会修复示例。
  • 你不能轻易添加自己的构造函数来做一个现有的std类(继承很麻烦)。您可以做的是为DoItDoItAgain 提供重载。

标签: c++ constructor default std-pair


【解决方案1】:

您不能轻易地将自己的构造函数添加到 std::pair(从它继承并不是一个真正的选择)。

但是你可以提供一个小函数来构造它并相应地重载它:

Nomnom nomnom(const std::string& s) { return Nomnom(-1, s); }
Nomnom nomnom(int i) { return Nomnom(i, ""); }

您还可以重载 DoItDoItAgain 并转发一个通用实现。

void DoIt(const Nomnom& n) { /* ... */ }
void DoIt(int i) { DoIt(Nomnom(i, "")); }

【讨论】:

  • 哦呵呵,有道理。我想过这样做,但出于某种原因决定不这样做,因为我想要一个构造函数,但是对于使用它的代码来说,它看起来很相似
  • Claudia, make_nomnom 也是个好名字,但我选择了较短的版本。但是,如果您的 Nomnom 真的有一些不变量,而不仅仅是一对,那么您真的想考虑使用一个类。
【解决方案2】:

原始问题

除了为 Nomnom 定义一个类,提供一个单参数构造函数,然后提供调用 std::pair 构造函数的零参数和双参数构造函数之外,还有什么方法可以实现这一点?

你可以有更多的构造函数重载

ClassNoms() : one(std::make_pair(-1,"") {}
ClassNoms(int id) : one(std::make_pair(id,"") {}
ClassNoms(const std::string& name) : one(std::make_pair(-1,name) {}
ClassNoms(int id, const std::string& name) : one(std::make_pair(id,name) {}

更新:

有没有什么办法可以实现这一点,除了为Nomnom 定义一个类而不是typedef,提供一个单参数构造函数,然后提供调用@987654324 的零参数和双参数构造函数@构造函数?

typedef std::pair<int, std::string> Nomnom;

您不能为 typedef'd NomNom 提供默认构造函数,因为 std::pair&lt;&gt; 没有提供默认构造函数。要实现这一点,您需要从std::pair&lt;&gt; 继承一个类NomNom,或者恕我直言,更好地为其提供一个包装器,并使用这个:

template<typename First,typename Second>
struct DefaultValuedPair {
     std::pair<First,Second> value;
     First& first;
     Second& second;
     DefaultValuedPair
        ( const First& first = First()
        , const Second& second = Second()
        ) 
     : value(std::make_pair(first,second))
     , first(value.first)
     , second(value.second) {}
     DefaultValuedPair(const std::pair<First,Second>& rhs) 
     : value(rhs)
     , first(value.first)
     , second(value.second) {}
     // Add more implicit type conversions for std::pair<First,Second>
     // as necessary ...
};

还有

typedef DefaultValuedPair<int, std::string> Nomnom;

你可以有一个像构造函数一样起作用的函数,但是它不能被命名和调用为NomNom(声明的)类型的纯等价物,这不会给出任何优于使用std::make_pair()

建议的包装器解决方案可能会对重构使用NomNom 的现有代码的考虑产生巨大影响,并且应该与此类无缝协作。如前所述,只是替换/伪装std::make_pair 本身,没有任何意义恕我直言。

【讨论】:

  • 你检查我的编辑,这不是我要问的。假装我从来没有提到过ClassNoms
  • @Claudiu 是同样的想法。只需使用make_pair,如下所示。
  • 我问是否有办法做到这一点除了将 typedef 更改为一个类,然后必须提供所有构造函数。您的回答只是显示了如何提供所有构造函数..
  • 啊,好吧,看起来那是不!
  • @Claudiu 好的,我为包装类提出了更可靠的建议。
【解决方案3】:

如果您使用的是 C++11,您可以只从与 using 配对的构造函数继承。我认为这是解决您的问题的最干净的解决方案。

您是否有不想从 std::pair 继承的原因(除了不想重新实现 std::pair 的构造函数)?

#include <utility>
#include <string>
#include <iostream>

/**
 * Nomnom is just a std::pair with a customized constructor.
 */
class Nomnom : public std::pair <int, std::string> {
  typedef std::pair<int, std::string> parent_type; // For convenience

  public:
  using parent_type::parent_type; // Inherit Pair's constructors
  Nomnom (std::string S) : parent_type(0, S) {} // And add our own
  Nomnom (char const * S) : parent_type(0, S) {} // Handle C strings...
};

/**
 * Show that we can just use Nomnom as a std::pair...
 */
template <class FIRST, class SECOND> 
std::ostream & operator<< (std::ostream & out, std::pair <FIRST, SECOND> const & print_me) {
  return out << print_me.first << ", " << print_me.second << '\n';
}

/**
 * Use this to test initizlization while passing to a method
 */
void foo (Nomnom const & print_me) {
  std::cout << print_me;
}

int main (void) {
  std::cout << Nomnom("Hello!");
  std::cout << Nomnom(5, "World!");
  foo ("Mouse!");

  return 0;
}

【讨论】:

  • 哦,太好了,不知道using。起初没有理由,但当我尝试它时,我意识到有一组函数可以在各种类型上运行,包括pair&lt;T1,T2&gt;T,所以如果我有一个T从对继承,我得到一个模棱两可的分辨率错误
  • 让我补充一点:从没有虚拟析构函数的类型继承是个坏主意。这适用于几乎所有标准库类型。此外,这打破了库可能为pair/tuple 接口提供的各种模板特化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 2014-07-28
相关资源
最近更新 更多