0

For below code:

template < class _InIt,
           class _Ty,
           class _Fn2 > inline
_Ty accumulateSimplePtr(_InIt _First, _InIt _Last, _Ty _Val, _Fn2 _Func)
{
    // return sum of _Val and all in [_First, _Last), using _Func
    for (; _First != _Last; ++_First)
    {
        if (is_class<std::iterator_traits<_InIt>::value_type>::value)
            _Val = _Func(_Val, (*_First)());
        else
            _Val = _Func(_Val, *_First);//This line doesn't work...
    }
    return (_Val);
}

I want the code to work both for _InIt pointing to double, and for pointing to a class. If pointing to a class, I will use (*_First)() to get the data(assuming the class has operator() that returns double), otherwise I simply use *_First to get the data.

Is there any way to use boost::is_class or any other ways to do this?

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Michael
  • 673
  • 2
  • 5
  • 23

2 Answers2

1

I don't think this is a good idea. I'd prefer to write two different functions to handle two different types like this:

// Also, note, your function is very similar to 
// std::accumulate from numeric header

std::vector<Foo> v1{3.14159, 1.77245385};
std::accumulate
(
    v1.begin(), v1.end(), 0.,
    [] (double i, const Foo& f) { return i + f(); }
);

std::vector<double> v2{3.14159, 1.77245385};
std::accumulate(v2.begin(), v2.end(), 0.);

But if you don't want to do this, I'd suggest write a function wrapper:

namespace detail
{

template <class F, class V, class T>
auto function_wrapper(F&& f, V&& v, T&& t)
    ->  typename std::enable_if
        <
            std::is_class<typename std::remove_reference<T>::type>::value,
            typename std::remove_reference<V>::type
        >::type
{
    return std::forward<F>(f)(std::forward<V>(v), std::forward<T>(t)());
}

template <class F, class V, class T>
auto function_wrapper(F&& f, V&& v, T&& t)
    ->  typename std::enable_if
        <
            !std::is_class<typename std::remove_reference<T>::type>::value,
            typename std::remove_reference<V>::type
        >::type
{
    return std::forward<F>(f)(std::forward<V>(v), std::forward<T>(t));
}

} // namespace detail

template < class _InIt,
           class _Ty,
           class _Fn2 > inline
_Ty accumulateSimplePtr(_InIt _First, _InIt _Last, _Ty _Val, _Fn2 _Func)
{
    for (; _First != _Last; ++_First)
        _Val = detail::function_wrapper(_Func, _Val, *_First);
    return (_Val);
}

class Foo
{
public:
    Foo(double d): _d(d)
    {

    }

    double operator() () const
    {
        return _d;
    }

private:
    double _d;
};


int main()
{
    auto f = [] (double a, double b) { return a + b; };

    std::vector<Foo> v1{3.14159, 1.77245385};
    std::cout << accumulateSimplePtr(v1.begin(), v1.end(), 0., f) << std::endl;

    std::vector<double> v2{3.14159, 1.77245385};
    std::cout << accumulateSimplePtr(v2.begin(), v2.end(), 0., f) << std::endl;

    return 0;
}
awesoon
  • 32,469
  • 11
  • 74
  • 99
1

You can use std::enable_if, applied to the return type of the template function, to direct the compiler to instantiate different functions depending on whether the value_type is a class or not, like this:

#include <type_traits>
#include <iterator>

// Compiler will choose this one when the value_type is a class
template < class _InIt,
           class _Ty,
           class _Fn2
>
typename std::enable_if<
    std::is_class<
        typename std::iterator_traits<_InIt>::value_type
    >::value,
    _Ty
>::type
accumulateSimplePtr(_InIt _First, _InIt _Last, _Ty _Val, _Fn2 _Func)
{
    // return sum of _Val and all in [_First, _Last), using _Func
    for (; _First != _Last; ++_First) {
        _Val = _Func(_Val, (*_First)());
    }
    return (_Val);
}

// Compiler will choose this one when the value_type is not a class
template < class _InIt,
           class _Ty,
           class _Fn2
>
typename std::enable_if<
    !std::is_class<
        typename std::iterator_traits<_InIt>::value_type
    >::value,
    _Ty
>::type
accumulateSimplePtr(_InIt _First, _InIt _Last, _Ty _Val, _Fn2 _Func)
{
    // return sum of _Val and all in [_First, _Last), using _Func
    for (; _First != _Last; ++_First) {
        _Val = _Func(_Val, *_First);
    }
    return (_Val);
}

Read about std::enable_if here

The way that you tried to do this does not work because it requires the compiler to compile both the *is_a_class" and the is_not_a_class branches of the if, even though they cannot both compile. std::enable_if causes only the correct function to be compiled.

Please note that you ought not to use identifiers beginning with an underscore and uppercase letter as all such identifiers are reserved for the compiler and its library. See here.

Community
  • 1
  • 1
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182