tags:

views:

73

answers:

3

Working on Windows with VS2005 and struggling to understand the error messages I'm getting. If this question has been asked before, I'm sorry. I couldn't find it.

Class I'm testing:

#include <functional>
using std::unary_function;

template<typename F, typename G>
struct UnaryConvolution : unary_function<typename G::argument_type,typename F::result_type>{
    UnaryConvolution(const F &_f, const G &_g) : m_F(_f), m_G(_g){}

    result_type operator()(const argument_type &_arg){
        return m_F( m_G( _arg ) );
    }

    F m_F;
    G m_G;
};

unit test I've written:

using std::bind2nd;
using std::equal_to;
using std::less;

bool unaryConvolution_test(){
    UnaryConvolution obj(bind2nd( equal_to<bool>(), true ), bind2nd( less<int>(), 5 ));

    return obj( 3 );
}

and errors I'm getting:

  1. error C2955: 'UnaryConvolution' : use of class template requires template argument list
  2. error C3848: expression having type 'UnaryConvolution' would lose some const-volatile qualifiers in order to call '_Result UnaryConvolution::operator ()(const _Arg &)'
  3. error C2514: 'UnaryConvolution' : class has no constructors

even adding the line int val = 3 and then passing val has no effect. (BTW, project is forbidden to use Boost or any 3rd party library. Don't ask, I try not to.)

+5  A: 

Just as the first error message says, you need to specify the template arguments to UnaryConvolution:

UnaryConvolution<SomeF, SomeG> obj(...);

Of course, spelling out the result of std::bind2nd is not exactly fun. A convenience function might help:

template<typename F, typename G>
inline UnaryConvolution<F,G> MakeUnaryConvolution(const F &_f, const G &_g)
{return UnaryConvolution<F,G>(f,g);}

But now you have the problem of having to find something to bind the result to:

??? obj = MakeUnaryConvolution( bind2nd(equal_to<bool>(),true)
                              , bind2nd(less<int>(),5) );

C++1x lets you get away with auto obj = .... However, VS2005 doesn't support that yet. I see two possibilities to do this:

  1. Make that type a template argument of a function that does the actual work.

    template< typename F >   
    bool doUnaryConvolution_test(F f, int i)
    {
      return f(i);
    }
    bool unaryConvolution_test()
    {
      return doUnaryConvolution_test( 
         MakeUnaryConvolution( bind2nd(equal_to<bool>(),true)
                             , bind2nd(less<int>(),5) ) );
    }
    
  2. Use std::tr1::function (not sure that's available for VS2005) or boost::function.

    std::tr1::function<bool(int)> obj = MakeUnaryConvolution( 
                                           bind2nd(equal_to<bool>(),true), 
                                           bind2nd(less<int>(),5) );
    
sbi
+6  A: 

UnaryConvolution is a class template, not a class. You need to specify the template arguments with which to instantiate the template. For example,

UnaryConvolution<
    function<bool(bool)>, 
    function<bool(int)>
> obj(bind2nd( equal_to<bool>(), true ), bind2nd( less<int>(), 5 ));

(I've used function, which you can get from Boost, C++ TR1, or C++0x, in place of the actual return type of bind2nd, which isn't quite as straightforward. If you don't have access to some implementation of function, then you'll need to go figure out the return type of bind2nd)

James McNellis
Often the easiest way (without `function`) is to create a "named constructor" - see `std::make_pair()` for an example. A "named constructor" can use Template Argument Deduction to figure out the return type from the arguments given. This is especially useful if you don't need the precise type anyway, but only an object. A `unary_function` like this is a good example, you'll likely pass it to a generic algorithm.
MSalters
@MSalters: I agree; sbi had already posted an excellent example of that, though.
James McNellis
+2  A: 

UnaryConvolution is a class template, so you need to provide template arguments when using it. However, when dealing with functors or function pointers, providing the arguments can be cumbersome, this is why we often rely on template argument deduction by making an helper function:

template <typename F, typename G>
UnaryConvolution<F, G> make_UnaryConvolution(const F & f, const G & g)
{
    return UnaryConvolution<F, G>(f, g);
}
Luc Touraille
Thanks for the tip. Very similar to what they do for back_inserter and such, no? I like it and this is how I'm now using things.
wheaties
Exactly, this kind of helper functions is used very frequently in libraries with class templates, such as the Standard library or some Boost libraries.
Luc Touraille