add constructor to templated class
I have a templated class holding a value. Is it possible to add a constructor to the class to allow implicit conversion like in the example below?
Or is there a better way to do this?
#include <string>
template<typename T>
class Value
{
public:
Value(const T& value) : m_value(value) { };
private:
T m_value;
};
// I thought adding something like this would do the trick but it does not work:
/*
template<>
class Value<std::string>
{
public:
Value(const char *sz) : m_value(sz) { };
}
*/
void f(const Value<std::string> &s)
{
}
int main()
{
f(std::string("hello"));
// I want to have this workin开发者_Python百科g:
f("hello");
}
Calling f(const Value<std::string>&)
with a string literal would require two user-defined conversions (const char[]
==>std::string
==>Value<std::string>
) in order to match the function parameters, while the standard only allows one.
I see two possibilities to solve that: either overload the constructor or overload f()
.
Assuming you're asking about the former because the latter isn't possible, there are several ways to overload the constructor.
You could exploit the fact that member functions of a class template are only compiled if they are invoked, and add a constructor that only compiles when T
is of a certain type. Of course, if users of your template invoke it for other types, this will result in an error.
However, instead of seeing a problem in this you could embrace it by making the constructor a member template:
template<typename U>
Value(const U& value) : m_value(value) { };
That way, whatever can be converted into T
(as well as T
itself, of course) is allowed for U
.
Or you could specialize the class for std::string
. Unfortunately, you would then have to specialize the whole class as there is no selective specialization of individual members. So in this case you might want to move all the code into a common (possibly private
base class of Value
, with the Value
base template just defining constructors that forward to the base class' constructors, and a specialization Value<std::string>
which adds another constructor taking const char*
.
You can't. This is an explicit decision by the C++ designers. The reason is that the compiler would need to go hunting for possible conversions, and in general this would be an infinite hunt.
Sure, you think that const char[]
==> std::string
==> Value<std::string>
is logical. But the compiler doesn't have std::string
. It just has const char[]
==> ??? ==>Value<std::string>
, and it would need to find a type in the middle. For instance, there might be a class Foo
somewhere that has a Foo::Foo(const char*)
constructor, and an Foo::operator Value<std::string>() const
. That would work, too.
As you can see, there's nothing in either const char[]
or Value<std::string>
that points to Foo. Hence, the compiler would have to go on a blind hunt.
As the writer of Value
, you do have a choice, though. You can inform the compiler that a Value<std::string>
can be constructed from any type that std::string::string will accept:
template<typename T>
class Value
{
public:
Value(const T& value) : m_value(value) { };
// Intentionally not "explicit"
template<typename U> Value(const U& value) : m_value(value) { };
private:
T m_value;
};
Now, if you call void f(const Value<std::string> &s)
as f("hello")
, there's a single implicit conversion Value<std::string>::Value<const char*>(const char* const&)
You can't add to the class like that, but you can specialize the entire class.
Try this:
template<typename T>
class Value
{
public:
Value(const T& value) : m_value(value) { }
Value(const char* a): m_value(a){} // add this
private:
T m_value;
};
精彩评论