I'm learning C++ and templates to implement a configuration file reader ( http://www.adp-gmbh.ch/cpp/chameleon.html ) and I'm trying to make a template for a class with a std::string
as internal storage, which can return its internal value when assigning it to a variable (double, long double, float, string, uint16_t, uint32_t).
Platform: Win10 with Visual Studio Community 2015 Update 1
class HybridType {
public:
HybridType() {};
explicit HybridType(const std::string& value) : internalStr(value) {}
explicit HybridType(const char* value) : internalStr (std::string(value)) { }
template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
explicit HybridType(T numericValue) {
std::stringstream strstr;
strstr << std::setprecision(std::numeric_limits<T>::digits10 + 1) << numericValue;
internalStr = strstr.str();
}
operator std::string() const { return internalStr; }
template<typename T>
operator T() const {
if (std::is_same<T, std::uint16_t>::value) return std::stoi(internalStr);
if (std::is_same<T, std::uint32_t>::value) return std::stoul(internalStr);
if (std::is_same<T, std::uint64_t>::value) return std::stoul(internalStr);
if (std::is_same<T, double>::value) return std::stod(internalStr);
if (std::is_same<T, long double>::value) return std::stold(internalStr);
if (std::is_same<T, float>::value) return std::stof(internalStr);
return std::stoll(internalStr);
}
template<typename T> operator T*() { return internalStr.c_str(); }
private:
std::string internalStr;
};
When using it, I do the following:
uint32_t test = HybridType("50");
long double test2 = HybridType("50.0");
But when I compile this, I get a lot of warnings:
1> warning C4244: 'return': conversion from 'double' to 'uint32_t',
possible loss of data 1> see reference to function template
instantiation 'HybridType::operator T(void) const<uint32_t>' being
compiled 1> with 1> [ 1> T=uint32_t 1> ]
1> warning C4244: 'return': conversion from 'long double' to
'uint32_t', possible loss of data 1> warning C4244: 'return':
conversion from 'float' to 'uint32_t', possible loss of data 1>
warning C4244: 'return': conversion from '__int64' to 'uint32_t',
possible loss of data
I didn't really understand why I have these warnings (compiler cannot choose the appropriate type), because when I output my variables they seem to have the correct value?
Your problem is operator T()
. Look at it from the compiler's point of view. If you expand for uint32_t
you get:
operator uint32_t() const {
if (...) return std::stoi(internalStr);
if (...) return std::stoul(internalStr);
if (...) return std::stoul(internalStr);
if (...) return std::stod(internalStr);
if (...) return std::stold(internalStr); // Consider here
if (...) return std::stof(internalStr);
return std::stoll(internalStr);
}
Which looks to the compiler that you might be trying to return a long double from a function that is supposed to return uint32_t
. Of course, later on in the compilation process, that will get optimized down to:
operator uint32_t() const {
return std::stoul(internalStr);
}
... but by that time, the warning has been issued.
The fix is either:
The advantage of the formwerapproach
Templates are evaluated at compile time, thus you can't use if()
to select the appropriate conversion function at runtime (return types will conflict, hence the warnings).
You need to provide type specialized implementations for your cast operator instead:
class HybridType {
public:
// ...
template<typename T>
operator T() const {
static_assert(std::is_same<T, std::uint16_t>::value ||
std::is_same<T, std::uint32_t>::value
// ...
,"Casted to an unsupported type!");
}
// ...
};
template<>
HybridType::operator std::uint16_t() const {
return std::stoi(internalStr);
}
template<>
HybridType::operator std::uint32_t() const {
return std::stoul(internalStr);
}
// aso. ...
The answer by Martin Booner has already pointed out the core issue. I am going to suggest a simpler solution for what you want to accomplish in your class.
Using templates for classes and functions is worthwhile only when the implementation is generic. That's not true in your case. The logic to return a int
is different that logic to return a double
. Using a member function template for this is not the best use of templates.
template<typename T>
operator T() const {
if (std::is_same<T, std::uint16_t>::value) return std::stoi(internalStr);
if (std::is_same<T, std::uint32_t>::value) return std::stoul(internalStr);
if (std::is_same<T, std::uint64_t>::value) return std::stoul(internalStr);
if (std::is_same<T, double>::value) return std::stod(internalStr);
if (std::is_same<T, long double>::value) return std::stold(internalStr);
if (std::is_same<T, float>::value) return std::stof(internalStr);
return std::stoll(internalStr);
}
is not any simpler than:
operator std::uint16_t() const {
return std::stoi(internalStr);
}
operator std::uint32_t() const {
return std::stoul(internalStr);
}
operator std::uint64_t() const {
return std::stoul(internalStr);
}
operator double() const {
return std::stod(internalStr);
}
operator long double() const {
return std::stold(internalStr);
}
operator float() const {
return std::stof(internalStr);
}
operator long long() const {
return std::stoll(internalStr);
}
even though the latter is more verbose due the presence of more functions.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.