Search code examples
c++templatesvariadic-templatesimplicit-conversionstdtuple

char unexpected behaviour with templated methods


I am working on a problem that should (since its HW) be solved by variadic templates. My lack of understanding prevents me from solving following error.

The code is:

#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>

template<typename ... TL>
class LazySplitResult
{
public:
    LazySplitResult(TL ...pl) :storedParams(pl ...) {       }

    void doStuff()
    {
        // invoke method with inside parameters
        useStoredTuple(storedParams, std::index_sequence_for<TL...>());
    }
    template<typename T, typename T1, typename... Targs>
    void doInsideLogic(T&& value1, Targs&&  ... Fargs)
    {
        // there is some logic
        // for example this
        std::stringstream convert("0");
        // it works for string,double,int ... other types are not supported
        // so exception would be in place
        convert >> value1;
    }

    void doInsideLogic()
    {
    }       

private:
    template<std::size_t... Is>
    void useStoredTuple(const std::tuple<TL ...>& tuple,std::index_sequence<Is...>) {
        wrapInsideLogic(std::get<Is>(tuple) ...);
    }

    void  wrapInsideLogic(TL && ... args)
    {
        doInsideLogic(args...);
    }

    std::tuple<TL ...> storedParams;
};

template<typename ...TL>
LazySplitResult<TL ...> getResult(TL && ...pl)
{
    return LazySplitResult<TL...>(pl  ...);
}

int main()
{
    std::string x;
    int y;
    double z;

    // prepares an object
    auto lazyResult=getResult(x, '.', '-', y, 'x', z , 'a');

    // let it do its thing and set unset variables
    lazyResult.doStuff();

    std::cout << "x = " << x << ", y = " << y << ", z = " << z << std::endl;

    return 0;
}

The error message is here

error C2664: 'void LazySplitResult<std::string &,char,char,int &,char,double &,char>::wrapInsideLogic(std::string &,char &&,char &&,int &,char &&,double &,char &&)': cannot convert argument 2 from 'const char' to 'char &&'
source_file.cpp(40): note: Conversion loses qualifiers
source_file.cpp(18): note: see reference to function template instantiation 'void LazySplitResult<std::string &,char,char,int &,char,double &,char>::useStoredTuple<0,1,2,3,4,5,6>(const std::tuple<std::string &,char,char,int &,char,double &,char> &,std::integer_sequence<_Ty,0,1,2,3,4,5,6>)'

It is copied from rextester here.

The main part of the HW is to parse a formula and save the result in the variables like this:

// declare variables and initialize class with formula

parse(x, '.', '-', y, 'x', z , 'a');   
std::cout << "x = " << x << ", y = " << y << ", z = " << z << std::endl;

The code does the same, except it does it in a lazy fashion so the parameters needs to be stored in a tuple for later use.

I tried to implement the same logic, except the laziness, without employing tuples and class with a success. It can be observed here. No error describind conversion errors was raised.

Can you please help me? I am completely lost. The entry method invocation is the same, the variadic template method that process the parameters have the same arguments... Only difference seems to be the tuple and class inbetween.


Solution

  • First, there is a useless template parameter T1 in your doInsideLogic function template. Remove it.

    The parameter of wrapInsideLogic is not a forwarding reference since TL is known and is not deduced. Its argument std::get<Is>(tuple) is of type const TL& since tuple is of type const std::tuple<TL...>&, thus does not match the parameter type TL&&. To fix this, there are two choices:

    1. Use forwarding reference, i.e.

      template <typename... Targs>
      void wrapInsideLogic(Targs&&... args)
      {
          doInsideLogic(std::forward<Targs>(args)...);
      } 
      
    2. Use reference to const, i.e.

      void wrapInsideLogic(const TL&... args)
      {
          doInsideLogic(args...);
      }