In the following code, the variable number of arguments assume a string variable in itself but it does not happen for string literals
#include <iostream>
#include <string>
#include <memory>
template <typename MyType, typename StringT=std::string, typename... ArgsT,
typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
std::shared_ptr<MyType> my_function(const StringT& name, ArgsT&&... args) {
std::cout << "name: " << name << std::endl;
auto t = std::make_shared<MyType>(5);
return t;
}
template <typename MyType, typename... ArgsT>
std::shared_ptr<MyType> my_function(ArgsT&&... args) {
auto t = my_function<MyType>("default name", std::forward<ArgsT>(args)...);
return t;
}
int main() {
std::string myvar= "something";
auto ret = my_function<int>("something");
auto ret2 = my_function<int, std::string>(myvar);
auto ret3 = my_function<int>(myvar);
auto ret4 = my_function<int>(42);// 42 could be anything
return 0;
}
Prints:
name: something
name: something
name: default name
name: default name
How to avoid this and call the first constructor in the third my_function<int>(myvar)
call as well? How to print "something" in all the cases except the last one?
You are using SFINAE to enable the 1st function only if std::string
can be constructed from the 1st parameter type. So, you should then use SFINAE to disable the 2nd function for the same parameter type, eg:
#include <iostream>
#include <string>
#include <memory>
template <typename MyType, typename StringT=std::string, typename... ArgsT,
typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
std::shared_ptr<MyType> my_function(const StringT& name, ArgsT&&... args) {
std::cout << "name: " << name << std::endl;
auto t = std::make_shared<MyType>(5);
return t;
}
template <typename MyType, typename Arg1T, typename... ArgsT,
typename = std::enable_if_t<!std::is_constructible_v<std::string, Arg1T>>>
std::shared_ptr<MyType> my_function(Arg1T&& arg1, ArgsT&&... args) {
auto t = my_function<MyType>("default name", std::forward<Arg1T>(arg1), std::forward<ArgsT>(args)...);
return t;
}
int main() {
std::string myvar= "something";
auto ret = my_function<int>("something");
auto ret2 = my_function<int, std::string>(myvar);
auto ret3 = my_function<int>(myvar);
auto ret4 = my_function<int>(42);// 42 could be anything
return 0;
}
Output:
name: something
name: something
name: something
name: default name
If you need to handle the case where no input parameters are passed in, then simply add a 3rd overload for that case:
template <typename MyType>
std::shared_ptr<MyType> my_function() {
auto t = my_function<MyType>("default name");
return t;
}