I have a function set_data
which I have to implement in different ways for different types it takes in. For example, this is trying to implement two overloads based on input type. If it is fundamental, non void
and non nullptr_t
, then handle it in first implementation. If it is a std::string
or a char
buffer, handle it second way.
struct field
{
template<typename TDataType, typename=void>
void set_data(TDataType data, size_t index = 0);
};
template<typename TDataType, typename = typename
std::enable_if< std::is_fundamental<TDataType>::value &&
std::is_same<TDataType, nullptr_t>::value == false &&
std::is_same<TDataType, void>::value == false>::type>
void field::set_data(TDataType data, size_t index /* = 0 */)
{
}
template<typename TDataType, typename = typename
std::enable_if< std::is_same<std::string const &, TDataType> ||
std::is_same<char const *, TDataType>>::type>
void field::set_data(TDataType data, size_t index /* = 0 */)
{
}
Then I call like :
field f;
int x = 10;
f.set_data(x);
And the compiler throws an error at me.
defs.h(111): error C2995: 'void messaging::field::set_data(TDataType,size_t)' : function template has already been defined
How do I resolve this?
On Visual studio 2013
You're trying to define the overloads outside of the class, which is of course not allowed. Or you're maybe trying to provide two partial specialisations for the member function template, but function templates cannot be partially specialised.
You need to get rid of the common declaration and move the two overloads into the class. You have to make them distinguishable, however. You can do that by giving one of them an extra template parameter (and add some missing ::value
to the second overload):
struct field
{
template<typename TDataType, typename = typename
std::enable_if< std::is_fundamental<TDataType>::value &&
std::is_same<TDataType, std::nullptr_t>::value == false &&
std::is_same<TDataType, void>::value == false>::type>
void set_data(TDataType data, size_t index = 0)
{
}
template<typename TDataType, typename = void, typename = typename
std::enable_if< std::is_same<std::string const &, TDataType>::value ||
std::is_same<char const *, TDataType>::value>::type>
void set_data(TDataType data, size_t index = 0)
{
}
};
Live example
Additionally, note that if the function template's argument is pass-by-value, the template argument can never be deduced to a reference type. So is_same<std::string const &, TDataType>
can only ever be true if the template argument std::string const &
is specified explicitly on the call site. You might want to replace it with just checking for std::string
. It might even be worthwhile to throw in some std::remove_reference
and std::remove_cv
to correctly handle explicitly specified template arguments.
The usual use of enable_if
for SFINAE on (member) functions (as in your example) is on the return type (rather than an additional template parameter). This would give you
struct field
{
template<typename TDataType>
typename std::enable_if< std::is_fundamental<TDataType>::value &&
!std::is_same<TDataType, std::nullptr_t>::value &&
!std::is_same<TDataType, void>::value>::
type field::set_data(TDataType data, size_t index = 0)
{
}
template<typename TDataType>
typename std::enable_if< std::is_same<std::string const&, TDataType>::value ||
std::is_same<char const*, TDataType>::value>::
type field::set_data(TDataType data, size_t index = 0)
{
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With