error: type / value mismatch at argument X in template parameter list

2

I try to create a backpack , a template class that acts as wrapper of another, and make calls to a callable provided in one of its arguments.

I would like the callable to be both a class that supports operator( ) , and a normal function.

All the core of the matter is a single function, void call( ) . As C ++ 11 does not support the specialization of member functions, I have created a template base class, which offer that single function. In the daughter class is where all the necessary functionality will be.

To the point. I tried

#include <iostream>
#include <functional>

template< typename T, typename F, bool IC, bool IF > struct Base;

// Especialización para VOID
template< typename T > struct Base< T, void, false, false > {
  void call( const T &, signed ) const { }
};

// F es una clase.
template< typename T, typename F > struct Base< T, F, true, false > {
  void call( const T &data, signed inc = 1 ) const {
    static F imp{ };

    imp( data, inc );
  }
};

// ESTO FALLA ESTREPITOSAMENTE.
//
// F es una función
template< typename T, typename F > struct Base< T, F, false, true > {
  void call( const T &data, signed inc = 1 ) const {
    F( data, inc );
  }
};

// Clase real, la que usamos.
template< typename T, typename F = void, bool IC = ::std::is_class< F >::value, bool IF = ::std::is_function< F >::value > struct Test :
public Base< T, F, IC, IF > {
  T value;

  Test< T, F, IC, IF >( ) : value( ) { Base< T, F, IC, IF >::call( value, 1 ); }
};

// Callable de prueba. Una clase.
struct Counter {
  template< typename T > void operator( )( const T &, signed i ) { ::std::cout << "Counter: " << i << "\n"; }
};

// Callable de prueba. Una función no-miembro.
void FCounter( const int &, signed inc ) { ::std::cout << "FCounter: " << inc << "\n"; }

int main( void ) {

  Test< int > t1;           // CASO 1
  Test< int, Counter > t2;  // CASO 2
  Test< int, FCounter > t3; // CASO 3

  //Test< int, []( int, signed i ) { std::cout << "lambda: " << i << "\n"; } > t4;

  return 0;
}

Within the main( ) are the current test cases. The 1 and the 2 are passed without problems, but the 3 ...

  

prog.cc:40:23: error: type / value mismatch at argument 2 in template parameter list for 'template struct Test'
     Test < int, FCounter > t3;
  prog.cc:40:23: note: expected a type, got 'FCounter'
  prog.cc:40:23: error: template argument 3 is invalid
  prog.cc:40:23: error: template argument 4 is invalid

  • How do I do it for case 3? Use a non-member function.
  • And, in an abuse ...

  • How would it be for case 4? Use a lambda . Or, better yet, something compatible with std::function< void( const T &, signed ) > .
  • asked by Trauma 14.09.2017 в 14:12
    source

    1 answer

    3

    Problem.

    expected a type, got 'FCounter'
    

    This error is saying that a type was expected and an instance has been passed to it. FCounter is not a type but a function, the type of FCounter is void( const int &, signed ) . To transform an instance into a type you must use the operator decltype :

    Test< int, decltype(FCounter) > t3; // CASO 3
    //         ^^^^^^^^^^^^^^^^^^ <--- void( const int &, signed )
    

    Solving this problem gives us the opposite error!:

    template< typename T, typename F > struct Base< T, F, false, true > {
      void call( const T &data, signed inc = 1 ) const {
        F( data, inc );
     // ^^^^^^^^^^^^^^ <--- F es un tipo, no una instancia
      }
    };
    

    Now we need an instance (not a type) to make the call, for this it must be provided in the constructor:

    template< typename T, typename F > struct Base< T, F, false, true > {
      F imp;
      Base(F f) : imp{f} {}
      void call( const T &data, signed inc = 1 ) const {
        imp( data, inc );
     // ^^^ <--- Instancia, no tipo
      }
    };
    

    But this will make the class Test fail because it can now inherit from a specialization that requires a constructor when it was not previously required in any specialization.

    Solution.

      

    I try to create a backpack , a template class that acts as wrapper of another, and make calls to a callable provided in one of its arguments.

         

    I would like the callable to be both a class that supports operator( ) , and a normal function.

    I understand from your requirements that you need to be able to call any function with any signature, but in your statement all calls are const T & as the first parameter and signed as the second parameter, so your backpack it could be:

    template <typename type_t, typename function_t>
    struct mochila
    {
        mochila(function_t function) : f{function} {}
    
        void call( const type_t &data, signed inc = 1 ) const
        {
            f(data, inc);
        }
    
        function_t f;
    };
    

    Unfortunately that mochila would not accept free functions (you can not have a function_t that is a free function, it should be a pointer ) so we must specialize for free functions:

    template <typename type_t>
    struct mochila<type_t, void( const type_t &, signed )>
    {
        using function_t = void( const type_t &, signed );
    
        mochila(function_t function) : f{function} {}
    
        void call( const type_t &data, signed inc = 1 ) const
        {
            f(data, inc);
        }
    
        function_t *f;
    };
    

    With these templates, we can modify your example:

    mochila<int, Counter> m1(Counter{});
    mochila<int, decltype(FCounter)> m2(FCounter);
    
    auto lambda = []( int, signed i ) { std::cout << "lambda: " << i << "\n"; };
    mochila<int, decltype(lambda)> m3(lambda);
    
    m1.call(1);
    m2.call(2);
    m3.call(3);
    

    Creating the following output:

    Counter: 1
    FCounter: 1
    lambda: 1
    

    You can see the code working in Wandbox 三 へ (へ ਊ) へ ハ ッ ハ ッ .

        
    answered by 14.09.2017 / 14:59
    source