Function call with template parameter

Viewed 118

I have written a simple class template:

template <class T>
class my_class
{
public:
    my_class(T value)
        : my_Value(value)
    {
    }
private:
    T my_Value;
};

Now I can use this template in a simple function signature like: my_function(my_class<std::string> my_string)

When I want to call the function I can easily use it:

auto my_instance = my_class<std::string>("my value");
my_function(my_instance);

But what I want to realize is a function call like this:

my_function("my value")

My class template should implicit do the conversion to the type of the template for me. I think I need some kind of operator overload.

std:optional can do this for example.

4 Answers

You can have only one implicit user-conversion, so your call with const char* is invalid.

There are several options,

  • add another constructor for my_class

    my_class(T value) : my_Value(value) {}
    
    template <typename U, std::enable_if_t<std::is_convertible<U, T>, int> = 0>
    my_class(U value) : my_Value(value) {}
    
  • add overload for my_function,

    void my_function(my_class<std::string> my_string)
    void my_function(const char* s) { return my_function(my_class<std::string>{s}); }
    
  • change call site to call it with std::string:

    my_function(std::string("my value"))
    
    using namespace std::string_literals;
    my_function("my value"s)
    

You have to add constructor accepting type convertible to your T.

The "classic" pre-C++20 way is to use SFINAE and std::enable_if:

template <typename T>
class my_class
{
public:
   template <typename U, typename = std::enable_if_t<std::is_constructible_v<T,U>>>
   my_class(U&& arg) : my_Value(std::forward<U>(arg)) 
   {}

...

Demo


With newest standard (C++20) you can use concepts and simplify your code:

template <typename T>
class my_class
{
public:
   template <typename U>
   my_class(U&& arg) requires(std::is_constructible_v<T,U>) 
      : my_Value(std::forward<U>(arg)) 
   {}

...

Or even simpler:

template <typename T, typename U>
concept constructible_to = std::constructible_from<U, T>;

template <typename T>
class my_class
{
public:
   template <constructible_to<T> U>
   my_class(U&& arg)
      : my_Value(std::forward<U>(arg)) 
   {}
...

As others have explained already, your problem is that you want to have two implicit conversions in a row (string literal (char const*) => std::string => my_class<std::string>) while only one is allowed.

Several ways how to reduce this chain to one have been explained already, but there is one more: Simply pass a std::string directly to your function instead of a string literal

using namespace std::string_literals;
my_function("my value"s);

Note the s which creates a std::string from a string literal. You need to use that namespace to have access to it.

Problem is described here:

Implicit conversions - cppreference.com

Order of the conversions

Implicit conversion sequence consists of the following, in this order:

  1. zero or one standard conversion sequence;

  2. zero or one user-defined conversion;

  3. zero or one standard conversion sequence.

When considering the argument to a constructor or to a user-defined conversion function, only a standard conversion sequence is allowed (otherwise user-defined conversions could be effectively chained). When converting from one built-in type to another built-in type, only a standard conversion sequence is allowed.

Your current code requires chain of two implicit user defined conversions: form const char * to std::string and then from std::string to my_class<std::string>.

To solve this you have to reduce this chain. So basically you need to provide constructor which will allow conversion of string literal to your my_class<std::string>.

PiotrNycz has provide solution:

template <class T>
class my_class
{
public:
    my_class(const T& value)
        : my_Value(value)
    {
    }

   template <typename U, typename = std::enable_if_t<std::is_constructible_v<T,U>>>
   my_class(U&& arg) : my_Value(std::forward<U>(arg)) 
   {}

private:
    T my_Value;
};

Demo

Related