Я написал простой шаблон класса:
template <class T>
class my_class
{
public:
my_class(T value)
: my_Value(value)
{
}
private:
T my_Value;
};
Теперь я могу использовать этот шаблон в простой сигнатуре функции, например: my_function(my_class<std::string> my_string)
Когда я хочу вызвать функцию, я могу легко ее использовать:
auto my_instance = my_class<std::string>("my value");
my_function(my_instance);
Но я хочу реализовать такой вызов функции:
my_function("my value")
Мой шаблон класса должен неявно выполнять преобразование в тип шаблона для меня. Я думаю, мне нужна какая-то перегрузка оператора.
std:optional
может сделать это, например.
Вы должны добавить конструктор, принимающий конвертируемый тип, в ваш T
.
«Классический» способ до C++20 — использовать SFINAE и 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))
{}
...
С новейшим стандартом (С++ 20) вы можете использовать концепции и упростить свой код:
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))
{}
...
Или еще проще:
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))
{}
...
У вас может быть только одно неявное пользовательское преобразование, поэтому ваш вызов с const char*
недействителен.
Есть несколько вариантов,
добавить еще один конструктор для 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) {}
добавить перегрузку для 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}); }
изменить сайт вызова, чтобы вызвать с помощью std::string
:
my_function(std::string("my value"))
using namespace std::string_literals;
my_function("my value"s)
Проблема описана здесь:
Неявные конверсии - cppreference.com
Порядок преобразований
Последовательность неявного преобразования состоит из следующего в указанном порядке:
ноль или одна стандартная последовательность преобразования;
ноль или одна пользовательская конверсия;
ноль или одна стандартная последовательность преобразования.
При рассмотрении аргумента конструктора или определяемой пользователем функции преобразования допускается только стандартная последовательность преобразования (в противном случае определяемые пользователем преобразования могут быть эффективно объединены в цепочку). При преобразовании из одного встроенного типа в другой встроенный тип допускается только стандартная последовательность преобразования.
Ваш текущий код требует цепочки из двух неявных определяемых пользователем преобразований: формы const char *
в std::string
и затем из std::string
в my_class<std::string>
.
Чтобы решить эту проблему, вы должны уменьшить эту цепочку. Итак, в основном вам нужно предоставить конструктор, который позволит преобразовать строковый литерал в ваш my_class<std::string>
.
PiotrNycz
предлагает решение:
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;
};
Как уже объясняли другие, ваша проблема заключается в том, что вы хотите иметь два неявных преобразования подряд (строковый литерал (char const*
) => std::string
=> my_class<std::string>
), в то время как разрешено только одно.
Несколько способов сократить эту цепочку до одной уже объяснялись, но есть еще один: просто передать std::string
прямо в вашу функцию вместо строкового литерала.
using namespace std::string_literals;
my_function("my value"s);
Обратите внимание на s
, который создает std::string
из строкового литерала. Вам нужно использовать это пространство имен, чтобы иметь к нему доступ.
Есть несколько вариантов: добавить еще один конструктор для
my_class
, добавить перегрузку дляmy_function
, изменить сайт вызова, чтобы вызывать его с помощьюstd::string
.