I am not sure how to ask this question. I did my research but I couldn't find my question.
For example, I have 3 variables which are FirstName, Surname and Email. If user give 3 input program will create this 3 parameter constructor which is;
Person::Person(string FirstName, string Surname, string Email){
this->FirstName=FirstName;
this->Surname=Surname;
this->Email=Email;
}
But if user doesn't give any input this constructor will work;
Person:Person(){
this->FirstName="No Info";
this->Surname="No Info";
this->Email="No Info";
}
The question is, How can I merge this two constructor? I mean I want only one constructor doing this overloaded constructor's job. Is it possible?
Actually the full question is this: Question
CodePudding user response:
If I understand you correctly, what you want are default parameters:
class Person {
public:
explicit Person(const std::string& firstName = "", const std::string& lastName = "", const std::string& email = ""):
m_firstName(firstName), m_lastName(lastName), m_email(email) {}
private:
std::string m_firstName;
std::string m_lastName;
std::string m_email;
};
One thing to mention, is that this approach means depending on which parameter you want to parse, you may need to fill them all.
E.g. if you want your Person to only have an email address, you'll still need to parse the first and last names as parameters, even if they're empty strings.
CodePudding user response:
The problem with giving default parameters is that the order is still fixed, so you cannot easily give any subset of the parameters, and rely on things like std::nullopt
, nullptr
, or an empty string to communicate that a parameter is missing.
Here is a guide trying to solve this exact thing: https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/
Something like the code below can be pretty decent and not that ugly in my opinion. You can add c 20 requires
to this to make sure all the arguments are supported in one of the set
functions. And making the members optional<>
allows you to know which were provided.
template<typename T, typename Tag>
class StrongType
{
public:
StrongType() = default;
StrongType(T&& t) : value(std::forward<T>(t)) {}
T value;
};
using FirstName = StrongType<std::string, struct FirstNameTag>;
using LastName = StrongType<std::string, struct LastNameTag>;
class Person
{
public:
template<typename... Args>
explicit Person(Args&&... args)
{
set_all(std::forward<Args>(args)...);
}
void set(FirstName first_name) { m_first_name = std::move(first_name); }
void set(LastName last_name) { m_last_name = std::move(last_name); }
private:
void set_all() {}
template<typename T, typename... Args>
void set_all(T&& first_argument, Args&&... rest_arguments)
{
set(first_argument);
set_all(std::forward<Args>(rest_arguments)...);
}
FirstName m_first_name;
LastName m_last_name;
};
int main()
{
Person person_1(FirstName("Jhon"), LastName("Jhonson"));
Person person_2(LastName("Peterson"));
Person person_3(FirstName("Jack"));
return 0;
}
CodePudding user response:
While default values for function parameters are way to go if you wat to allow partial mission of arguments, with modern C a delegation is possible:
class Person {
public:
explicit Person(const std::string& firstName,
const std::string& lastName, const std::string& email):
m_firstName(firstName), m_lastName(lastName), m_email(email) {}
Person() : Person ("No Info","No Info","No Info") {}
private:
std::string m_firstName;
std::string m_lastName;
std::string m_email;
};
Default arguments allow something like Person("Nick","Bridges")
. Delegating ones allow to re-use of same code in several constructors. Those approaches can be combined, of course.