With templates, how to distinguish between two parallel cases, for example, between floating-point types and integral types?

cppreference.com ( http://en.cppreference.com/w/cpp/types/enable_if#Notes ) notes that:

A common mistake is to declare two function templates that differ only in the default template arguments. This is illegal because the default template arguments are not part of the function template signature, and declaring two different function templates with the same signature is illegal.

struct T { enum { int_t,float_t } m_type; template <typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value> > T(Integer) : m_type(int_t) {} template <typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value> > T(Floating) : m_type(float_t) {} // error: cannot overload }; 

So, true ... So, what is the right approach to solve this problem and actually achieve what failed to achieve the above incorrect code?

+5
source share
4 answers

I believe this will work:

 #include <type_traits> struct T { enum { int_t,float_t } m_type; template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value>* = nullptr > T(Integer) : m_type(int_t) {} template <typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value>* = nullptr > T(Floating) : m_type(float_t) {} // now the overload is valid }; int main() { T t = int{}; T t2 = float{}; (void)t; (void)t2; } 

[live demo]

+5
source

Use tag sending:

 namespace tag { struct floating{}; struct integer{}; struct error{}; template<typename T, typename = void> struct get : error {}; template<typename T> struct get<T, std::enable_if_t<std::is_integral<T>::value>> : integer {}; template<typename T> struct get<T, std::enable_if_t<std::is_floating_point<T>::value>> : floating {}; } struct Foo { template<typename T> Foo(T&&, tag::floating){ } template<typename T> Foo(T&&, tag::integer){ } template<typename T> Foo(T&& t) : Foo(std::forward<T>(t), tag::get<std::decay_t<T>>{}) {} }; 

demo

+4
source

Use enable_if in another dummy argument in the constructor parameter list:

 struct T { enum { int_t,float_t } m_type; template <typename Integer> T(Integer, std::enable_if_t<std::is_integral<Integer>::value>* = nullptr) : m_type(int_t) {} template <typename Floating> T(Floating, std::enable_if_t<std::is_floating_point<Floating>::value>* = nullptr) : m_type(float_t) {} }; 
+1
source

I believe I have found another way to figure this out. This, unfortunately, does not work with floats due to restrictions on the use of floating point types in templates, but should work for most other cases (for example, to differentiate between signed and unsigned types):

 struct T { enum { signed_t,unsigned_t } m_type; template <typename Signed, std::enable_if_t<std::is_signed<Signed>::value, bool> = true > T(Signed) : m_type(signed_t) {} template <typename Unsigned, std::enable_if_t<std::is_unsigned<Unsigned>::value, bool> = true > T(Unsigned) : m_type(unsigned_t) {} }; 

Real-time example: http://ideone.com/xqp4nd

0
source

Source: https://habr.com/ru/post/1259561/


All Articles