Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with this use of std::enable_if?

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

like image 861
nakiya Avatar asked Jan 09 '23 12:01

nakiya


2 Answers

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.

like image 185
Angew is no longer proud of SO Avatar answered Jan 18 '23 10:01

Angew is no longer proud of SO


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)
    {
    }
};
like image 40
Walter Avatar answered Jan 18 '23 09:01

Walter