Reputation: 275
I'm breaking sweat over disecting function templates to three groups: one to produce functions that take integrals, one to take floating points and one to take any other (that ostringstream::<<
accepts). So far I can't even make it so that there are two groups, like so:
namespace my {
template<typename T>
struct logical_not
:
std::integral_constant<bool, !T::value>
{};
template <typename T>
using static_not = typename std::conditional<
T::value,
std::false_type,
std::true_type
>::type;
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if< std::is_integral<T>::value >::type* = 0)
{
// integral version (ostringstream method would be replaced by a simple algorithm that forms a string)
std::ostringstream os;
os << val;
return os.str();
}
template<typename T>
std::string to_string(
const T& val,
//typename std::enable_if< logical_not<std::is_integral<T>>::type >::type* = 0)
typename std::enable_if< static_not<std::is_integral<T>> >::type* = 0)
{
std::ostringstream os;
os.flags(std::ios::fixed);
os.precision(2);
os << val;
return os.str();
}
} // my
I copied the two negating functions from other answers, I left their names like so just so it's easier to distinguish. I find type traits incredibly confusing, I presume the error I get for logical_not is a wrong usage of that particular negater wrapper.
With logical_not error is Illegal type for non-type template parameter
and with static_not in the instance of a class pointer type: 'my::to_string': no matching overloaded function found
.
Please show me in the right direction if you can! My point is to add a faster implementation (without allocating for ostringstream instance) for integral types, adjust precision with floating point ones and have a function that handles the other types. Most likely no "negater" wrapper would be required for the final version, though I'd be curious why they won't work.
EDIT: I've realized I'd need 4 groups, 4th being boolan (since boolen is an integral type as well). So building on top of max66's answer, modifying one function and adding another, the intended functionality is achieved:
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if<
std::is_same<bool, T>::value
>::type* = 0)
{
return val ? "true" : "false";
}
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if<
std::is_integral<T>::value
&& (false == std::is_same<bool, T>::value)
>::type* = 0)
{
return "integral, but not bool";
}
Upvotes: 3
Views: 101
Reputation: 66230
It's simpler than you think.
#include <string>
#include <iostream>
#include <type_traits>
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if< std::is_integral<T>::value
>::type * = nullptr)
{ return "case integral"; }
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if< std::is_floating_point<T>::value
>::type * = nullptr)
{ return "case floating"; }
template<typename T>
std::string to_string(
const T& val,
typename std::enable_if< (false == std::is_integral<T>::value)
&& (false == std::is_floating_point<T>::value)
>::type * = nullptr)
{ return "case generic"; }
int main ()
{
std::cout << to_string(0) << std::endl; // print case integral
std::cout << to_string(0.0) << std::endl; // print case float
std::cout << to_string("0.000") << std::endl; // print case generic
}
Upvotes: 4