Reputation:
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 their default template arguments. This is illegal because default template arguments are not part of function template's 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 correct approach to solve this issue and actually achieve what the above incorrect code fails to achieve?
Upvotes: 6
Views: 290
Reputation:
I believe I have found a yet another way to sort this out. This sadly won’t work with floats due to the limitations of the use of floating point types within templates, but should work for most other cases nevertheless (such as differentiating 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) {}
};
Live example: http://ideone.com/xqp4nd
Upvotes: 0
Reputation: 13988
I believe this would 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;
}
Upvotes: 5
Reputation: 55415
Use enable_if
in another dummy argument in constructor's 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) {}
};
Upvotes: 1
Reputation: 16431
Use tag dispatching:
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>>{}) {}
};
Upvotes: 4