Невозможно назначить строковый литерал в коробке std::string vector
Это упрощенная версия моей системы типов:
#include <string>
#include <vector>
template<typename T>
class Box {
public:
Box(const T& value) : _value(value) {};
private:
T _value;
/* ... */
};
typedef Box<int> Int;
typedef Box<double> Double;
typedef Box<std::string> String;
int main(int argc, char* argv[]) {
String a("abc");
std::vector<String> b = { std::string("abc"), std::string("def") };
// error C2664: 'Box<std::string>::Box(const Box<std::string> &)' : cannot convert argument 1 from 'const char' to 'const std::string &'
std::vector<String> c = { "abc", "def" };
}
Пока a
и b
компилируются, c
нет, и причина в том, что я пытаюсь инициализировать из const char
. Это вызывает два вопроса:
-
Почему b
возможно, но не c
? Это из-за вложенного шаблона в std::vector<Box<std::string> >
?
-
Могу ли я сделать работу c
без разрушения общего механизма бокса (см. typedef Box<double> Double
?
Ответы
Ответ 1
c
настоящее время требуется 2 неявных пользовательских преобразования (const char [N]
→ std::string
→ String
), тогда как разрешено только одно.
Вы можете добавить конструктор шаблонов в Box
template<typename T>
class Box {
public:
Box() = default;
Box(const Box&) = default;
Box(Box&&) default;
~Box() = default;
Box& operator=(const Box&) = default;
Box& operator=(Box&&) = default;
template <typename U0, typename ...Us,
std::enable_if_t<std::is_constructible<T, U0, Us...>::value
&& (!std::is_same<Box, std::decay_t<U0>>::value
|| sizeof...(Us) != 0)>* = nullptr>
Box(U0&& u0, Us&&... us) : _value(std::forward<U0>(u0), std::forward<Us>(us)...) {}
private:
T _value;
/* ... */
};
Демо Demo2
Ответ 2
Глядя на исходный код только в основной части функции:
int main(int argc, char* argv[]) {
String a("abc");
std::vector<String> b = { std::string("abc"), std::string("def") };
// error C2664: 'Box<std::string>::Box(const Box<std::string> &)' :
// cannot convert argument 1 from 'const char' to 'const std::string &'
std::vector<String> c = { "abc", "def" };
}
Ваша первая строка кода:
String a("abc");
Использует typedef
версию Box<std::string>
, которую этот шаблон шаблона принимает const T&
, и поскольку эта версия шаблона ожидает std::string
, он использует конструктор std::string's
для построения std::string
из a const char[3]
, и все в порядке.
Следующая строка кода:
std::vector<String> b = { std::string("abc"), std::string("def") };
Является ли std::vector<T>
тем же выше. Таким образом, это работает, так как вы инициализируете vector<T>
с действительными объектами std::string
.
В вашей последней строке кода:
std::vector<String> c = { "abc", "def" };
Здесь вы объявляете c
как vector<T>
, где T
является typedef
версией Box<std::string>
, однако вы не инициализируете типы std::vector<T>
с помощью Box<std::string>
. Вы пытаетесь инициализировать его с помощью объектов const char[3]
или строковых литералов.
Вы можете попробовать сделать это для третьей строки: я не пытался скомпилировать это, но я думаю, что он должен работать.
std::vector<String> c = { String("abc"), String("def") };
EDIT - я хотел использовать конструктор для String
, а не std::string
сделал соответствующее редактирование.
Ответ 3
Вы можете использовать вспомогательную функцию, которая создает соответствующий тип:
template <typename T,typename R>
Box<T> make_boxed(const R& value){
return Box<T>(value);
}
может показаться дополнительным осложнением, что нужно указать T
, с другой стороны, вы можете использовать auto
для возвращаемого типа. Полный пример:
#include <string>
template<typename T>
class Box {
public:
Box(const T& value) : _value(value) {};
private:
T _value;
/* ... */
};
typedef Box<std::string> String;
int main(int argc, char* argv[]) {
auto a = make_boxed<std::string>("asd");
}