What is the standard convention when creating multiple default constructors with the same arguments?

I am making a class in C++ and I need two default constructors with the same arguments. The only argument they need to take is a vector. Depending on the data that is given in the vector I need to do something different and the way the data is given it is impossible to tell which constructor the user wants.

Should I create two default constructors, the first one takes a vector and int as arguments, the second takes a vector and char as argument. Then in the documentation explain which constructor you are using when you give me either an int or char.

Or should I use one default constructor that takes a vector and int, then in the documentation state that when creating your object, 0 for the int argument will give you one constructor and 1 for the int argument will use the other constructor.

Or is there a better way that I don't know about like using keywords or something with templates like this:
1
2
3
4
5
6
7
MyClass(firstOption, const vector<double> & sample1){
//default constructor
}

MyClass(secondOption, const vector<double> & sample2){
//different default constructor
}


then in main the programmer can distinguish by calling them like this:
1
2
MyClass firstObject(firstOption, vectOfData);
MyClass secondObject(secondOption, vectOfData):


Can I do something like this? What would it be called so I can look it up?
Last edited on
What is the standard convention when creating multiple default constructors with the same arguments?
There is no convention, since it's illegal.

Note: a default constructor is any constructor which can be called with zero arguments (with or without default arguments.)

Second, this looks like an XY problem ( http://xyproblem.info/ ): what problem are you trying to solve?
In other words, what is your use case: why do you want this?

Finally, tag-dispatch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# include <iostream>
# include <vector>

struct first_overload_t { explicit first_overload_t() = default; };
constexpr first_overload_t first_overload = first_overload_t();

struct second_overload_t { explicit second_overload_t() = default; }; 
constexpr second_overload_t second_overload = second_overload_t();

struct my_class {
  my_class(first_overload_t, std::vector<double> const &sample) 
  { std::cout << "first overload!\n"; };
  my_class(second_overload_t, std::vector<double> const &sample) 
  { std::cout << "second overload!\n"; }; 
};

int main() { 
  std::vector<double> sample {1.0, 2.0, 3.0};
  my_class m{first_overload, sample}; 
  my_class n{second_overload, sample}; 
}
Last edited on
Depending on the data that is given in the vector I need to do something different and the way the data is given it is impossible to tell which constructor the user wants.


If it's impossible to tell what the user wants, how do you write the program to call the right constructor?
Last edited on
First, a note: the default constructor is the constructor that takes no arguments, and there is at most one default for a class.


You could consider enumerations. See http://en.cppreference.com/w/cpp/language/enum
Solution #1 - Pass a bool

1
2
3
4
5
6
7
8
9
10
11
MyClass(bool firstWay, const vector<double> & sample)
{
	if (firstWay)
	{
		// construct the first way
	}
	else
	{
		// construct the second way
	}
}

The disadvantage with this approach is that you need to write both implementations in the same constructor and you get an additional branch. It's also not very clear what true or false means when constructing the objects.

1
2
MyClass firstObject(true, vectOfData);
MyClass secondObject(false, vectOfData);

The advantage is that you can easily let the decision on which construction method to use be a runtime decision.

1
2
bool decision = ...
MyClass firstObject(decision, vectOfData);


Solution #2 - Pass an enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum class ConstructionMethod
{
	first, second
};

MyClass(ConstructionMethod constructionMethod, const vector<double> & sample)
{
	switch (constructMethod)
	{
	case MyClassConstructionMethod::first:
		// construct the first way
		break;
	case MyClassConstructionMethod::second:
		// construct the second way
		break;
	}
}

This is almost the same as passing a bool but you can use more descriptive names than true and false which makes the code more readable when constructing the objects.

1
2
MyClass firstObject(MyClass::ConstructionMethod::first, vectOfData);
MyClass secondObject(MyClass::ConstructionMethod::second, vectOfData);

It also gets easier if you later want to add additional construction methods without breaking existing code.


Solution #3 - Pass empty class objects

1
2
3
4
5
6
7
8
9
10
11
12
class FirstConstructionMethod {};
class SecondConstructionMethod {};

MyClass(FirstConstructionMethod, const vector<double> & sample)
{
	// construct the first way
}

MyClass(SecondConstructionMethod, const vector<double> & sample)
{
	// construct the second way
}

This allows you to use two separate constructors, which is great. Construction of the objects should have the same level of readability as enums.

1
2
MyClass firstObject(MyClass::FirstConstructionMethod(), vectOfData);
MyClass secondObject(MyClass::SecondConstructionMethod(), vectOfData);

One small disadvantage with this approach is that it takes a little more work if you want to decide between the different construction methods at runtime.
Last edited on
Solution #4 - Use static member functions

1
2
3
4
5
6
7
8
9
10
11
12
13
static MyClass createFirst(const vector<double> & sample)
{
	MyClass obj;
	// construct obj the first way
	return obj;
}

static MyClass createSecond(const vector<double> & sample)
{
	MyClass obj;
	// construct obj the second way
	return obj;
}

This is quite nice actually. It's like having named constructors. Since the functions are members of the class you have full access to all the private members of the class. If you for example don't want the user to be able to use the default constructor (the one that takes no arguments) you can simply make it private and still use it from inside these function.

1
2
MyClass firstObject = MyClass::createFirst(vectOfData);
MyClass secondObject = MyClass::createSecond(vectOfData);
Last edited on
thanks everyone for your answers, I guess I didn't type what I meant correctly. I have a default constructor, but what i meant to say is I need two more constructors and both of these constructors take in the same argument. I will try Peter87's solutions out.
closed account (E0p9LyTq)
Two separate constructors can not have the exact same parameter list, that is illegal.
I will try Peter87's solutions out.

Or you could be, y'know, really radical, and answer the question mbozzi asked you. If we knew what it is you're actually trying to achieve, we could actually help you find the right solution.
@MikeyBoy, I appreciate the help but all I needed was to know if it was possible and what the idea is, I can figure it out from here, also I don't want anyone to do my work for me. I also wasn't specific so that if someone that is learning c++ needs to do something similar to this, this post will help and give them ideas to figure it out on their own.
The point is that, as mbozzi said, you're already fixated on implementing a specific solution to a problem - having two constructors for a class - when, in fact, there may be a much better, completely different solution to the thing you're trying to achieve. When the language doesn't support well the thing you're trying to implement, it's often because what you're trying to implement is not the best way to achieve the thing you're trying to write code for.

It often indicates that there is a different design, that would be a much better way of doing what you want. If you told us what you're trying to achieve, maybe we could help you find that better solution, rather than giving you convoluted ways to implement a poorer one.
Solution #5 - Use the constructor class idiom

Semantic information can be supplied as part of the named parameter idiom.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <vector>

struct im_a_dog { const std::vector <double> & xs; };
struct im_a_cat { const std::vector <double> & xs; };

struct MyClass
{
  MyClass( const im_a_dog& doggie )
  { 
    std::cout << "doggie\n";
  }
  
  MyClass( const im_a_cat& kitty )
  {
    std::cout << "kitty\n";
  }
};

int main()
{
  std::vector <double> primes { 2, 3, 5, 7, 11, 13 };
  MyClass cat( im_a_cat{ primes } );
  MyClass dog( im_a_dog{ primes } );
}

That said, the advice given you so far is spot-on: you have a design flaw in your thinking. You are giving your class the exact same data but expecting it to mean two different things? Confuddled design → confuddled behavior.
@MikeyBoy, I just figured out how to do what I wanted using only one constructor, I was over thinking it.
Topic archived. No new replies allowed.