This is a simplified version of my type system:
#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" };
}
While a
and b
compile, c
does not and the reason seems to be that I try to initialize from const char
. This raises two questions:
Why is
b
possible but notc
? Is it because of the nested template instd::vector<Box<std::string> >
?Can I make
c
work without destroying the general boxing mechanism (cf.typedef Box<double> Double
?
c
currently need 2 implicit user conversions (const char [N]
-> std::string
-> String
) whereas only one is allowed.
You may add template constructor to 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;
/* ... */
};
Looking at your source code in the main function part only:
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" }; }
Your first line of code:
String a("abc");
Is using the typedef
version of Box<std::string>
which this class template takes a const T&
and since this version of the template is expecting a std::string
it is using std::string's
constructor to construct a std::string
from a const char[3]
and this is okay.
Your next line of code:
std::vector<String> b = { std::string("abc"), std::string("def") };
Is a std::vector<T>
of the same above. So this works as well since you are initializing the vector<T>
with valid std::string
objects.
In your final line of code:
std::vector<String> c = { "abc", "def" };
Here are you declaring c
as a vector<T>
where T
is a typedef
version of Box<std::string>
however you are not initializing the std::vector<T>
with Box<std::string>
types. You are trying to initialize it with const char[3]
objects or string literals.
You can try doing this for the third line: I haven't tried to compile this but I think it should work.
std::vector<String> c = { String("abc"), String("def") };
EDIT -- I meant to use the constructor for String
and not std::string
made the appropriate edit.
You could use a helper function that creates the corresponding type:
template <typename T,typename R>
Box<T> make_boxed(const R& value){
return Box<T>(value);
}
it may seem like additional complication that one has to specify the T
, on the other hand you can use auto
for the returned type. Complete example:
#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");
}
来源:https://stackoverflow.com/questions/44108754/cant-assign-string-literal-to-boxed-stdstring-vector