Question

I'm using a pair type:

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

Example places it's used:

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

In my case it makes sense to offer a default value for the pair if it isn't specified. That is, I want to be able to do this:

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

and have it be equivalent to this:

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

I want to do this without defining two variants every time a Nomnom is used.

Is there any way to accomplish this short of defining a class for Nomnom instead of the typedef, providing a one-arg constructor, and then providing the zero-arg and two-arg constructors that call the std::pair constructors?

Was it helpful?

Solution

You cannot easily add your own constructor to std::pair (inheriting from it isn't really an option).

But you can provide a small function to construct it and overload it accordingly:

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

You could also overload DoIt and DoItAgain and forward on a common implementation.

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

OTHER TIPS

ORIGINAL QUESTION

Is there any way to accomplish this short of defining a class for Nomnom instead, providing a one-arg constructor, and then providing the zero-arg and two-arg constructors that call the std::pair constructors?

You could have more constructor overloads as

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) {}

UPDATE:

Is there any way to accomplish this short of defining a class for Nomnom instead of the typedef, providing a one-arg constructor, and then providing the zero-arg and two-arg constructors that call the std::pair constructors?

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

You can't provide a default constructor for your typedef'd NomNom, because std::pair<> doesn't provide one. To achieve this, you'll need to either inherit a class NomNom from std::pair<>, or IMHO better provide a wrapper for it, and use this one:

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 ...
};

And have

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

You could have a function that acts like a constructor, but it can't be named and called as pure equivalent for a NomNom (declared) type, and this wouldn't give any advantage over using std::make_pair().

The proposed wrapper solution might have a huge impact on considerations of refactoring already existing code that uses NomNom, and should work seamlessly with such. As mentioned, just replacing/disguising std::make_pair per se, makes no sense IMHO.

If you're using C++11, you can just inherit the constructors from pair with using. I think this is the cleanest solution to your problem.

Is there a reason (besides not wanting to re-implement std::pair's constructors) you don't want to inherit from 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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top