c++function-templates

How to handle variable number of arguments and string variables


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?


Solution

  • 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
    

    Online Demo

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

    Online Demo