使用新模板参数递归调用模板化函数

时间:2015-10-17 22:04:12

标签: c++ templates visual-c++ functional-programming

我正在尝试用c ++实现一些功能结构。想要实现一个功能,可以将任意级别的列表列表缩小。

itemans

上面的代码编译得很好,但是当我尝试用

调用函数时
template<typename T, typename R>
struct Fold{
    typedef R(*func)(T, R);
};


template<typename T>
T head(std::list<T> const& list) {
    return list.front();
}

template<typename T>
std::list<T> tail(std::list<T> list) {
    list.pop_front();
    return list;
}

template<typename T>
std::list<T> cons(T head, std::list<T> tail){
    tail.push_front(head);
    return tail;
}

template<typename T, typename ACCUM>
ACCUM foldl(typename Fold<T, ACCUM>::func function, ACCUM accum, std::list<T> list) {
    if(list.empty())
        return accum;

    return foldl(function, (*function)(head(list), accum), tail(list));
}

template<typename T, typename ACCUM>
ACCUM foldr(typename Fold<T, ACCUM>::func function, ACCUM accum, std::list<T> list) {
    if(list.empty())
        return accum;

    return (*function)(head(list), foldr(function, accum, tail(list)));
}

template<typename T>
std::list<T> reverse(std::list<T> list){

    struct LAMBDA{
        static std::list<T> reverse(T t, std::list<T> tList){
            return cons(t, tList);
        }
    };

    std::list<T> revTList;
    return foldl( static_cast<typename Fold<T, std::list<T>>::func>(&LAMBDA::reverse), revTList, list); 
}

template<typename T>
std::list<T> append(std::list<T> list1, std::list<T> list2) {
    struct LAMBDA{
        static std::list<T> append_lambda(T t, std::list<T> list){
            return cons(t, list);;
        }
    };

    return foldl( static_cast<typename Fold<T, std::list<T>>::func>(&LAMBDA::append_lambda), list2, reverse(list1));
}

template<typename T, typename Ty>
struct Flattener{
    static std::list<T> flatten(typename std::list<Ty> deepList){
        struct LAMBDA{
            static Ty flatten_lambda(Ty ty, Ty accum){
                return append(ty, accum);
            }
        };
        Ty ty;
        Ty flat = foldr( static_cast<typename Fold<Ty, Ty>::func>(&LAMBDA::flatten_lambda), ty, deepList);
        return Flattener::flatten(flat);
    }
};

template<typename T>
struct Flattener<T, T>{
    static std::list<T> flatten(std::list<T> list){
        return list;
    }
};

编译代码时出现了这个巨大的错误:

std::list<int> emptyList;
std::list<int> list1 = cons(1, cons(2, cons(3, cons(4, emptyList))));
std::list<int> list2 = cons(5, cons(6, cons(7, cons(8, emptyList))));

std::list<std::list<int>> emptyDeepList;
std::list<std::list<int>> deepList = cons(list1, cons(list2, emptyDeepList));
Flattener<int, std::list<int>>::flatten(deepList);

如果我删除了对error C2664: 'Flattener<T,Ty>::flatten' : cannot convert parameter 1 from 'std::list<T>' to 'std::list<T>' with [ T=int, Ty=std::list<int> ] and [ T=int ] and [ T=std::list<int> ] No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called list.h(212) : while compiling class template member function 'std::list<T> Flattener<T,Ty>::flatten(std::list<std::list<T>>)' with [ T=int, Ty=std::list<int> ] main.cpp(67) : see reference to class template instantiation 'Flattener<T,Ty>' being compiled with [ T=int, Ty=std::list<int> ] 的调用,则会编译代码。

我做错了什么? (因为我是c ++和模板编程的新手,所以一些解释也会非常有用)。

修改

试过这个。相同的错误。我想我正在做点什么。

Flattener::flatten

以下是此编译器的错误:

template<typename T, typename L>
struct Flattener{
    static std::list<T> flatten(L list){
        struct LAMBDA{
            static std::list<T> flatten_lambda(typename L1 l1, std::list<T> tList){
                return append(Flattener<T, L1>::flatten(l1), tList);
            }
        };

        std::list<T> tList;
        return foldl(&LAMBDA::flatten_lambda, tList, list);
    }
};

template<typename T>
struct Flattener<T, typename std::list<T>>{
    static std::list<T> flatten(std::list<T> list){
        return list;
    }
};

4 个答案:

答案 0 :(得分:5)

AS @tMJ指出,编写Flattener<T, T>将使编译错误消失,但Flattener::flatten方法将能够将两个级别仅压平两个深度列表(实际上当您尝试时错误将会返回展平m-nested list m >= 3,因为我们将再次出现类型不匹配的情况。)

为了使这项工作适用于m级std::list<std::list<...<std::list<int>...>>的列表,我们必须找到当前Ty容器的元素类型的方法。例如,如果Ty = std::list<std::list<std::list<int>>>那么 Ty 的当前元素实际上是std::list<std::list<int>>。这是 Ty 的类型,必须在递归的下一步中设置。

幸运的是,诸如std :: list之类的C ++容器具有静态value_type属性,这是容器的模板参数Type的同义词(简单来说它将返回元素的类型这个容器)。知道了这一点,我们可以通过以下方式解决您的问题:

template<typename T, typename Ty>
struct Flattener {
    static std::list<T> flatten(typename std::list<Ty> deepList) {
        struct LAMBDA {
            static Ty flatten_lambda(Ty ty, Ty accum) {
                return append(ty, accum);
            }
        };
        Ty ty;
        Ty flat = foldr(static_cast<typename Fold<Ty, Ty>::func>(&LAMBDA::flatten_lambda), ty, deepList);
        return Flattener<T, Ty::value_type>::flatten(flat);
    }
};

template<typename T>
struct Flattener<T, T> {
    static std::list<T> flatten(std::list<T> list) {
        return list;
    }
};

T等于Ty::value_type时递归将停止,Ty变为std::list<int>时会发生这种情况。此时Flattener<T, T>::flatten()将被执行,它将产生最终结果。

我使用三嵌套std::list测试了解决方案:

std::list<std::list<std::list<int>>> triple_empty_list;
std::list<std::list<std::list<int>>> triple_list = cons(deepList, cons(deepList, triple_empty_list));
std::list<int> result = Flattener<int, std::list<std::list<int>>>::flatten(triple_list);

P.S。为了澄清,这里没有发生递归。每次调用Flattener<T,Ty>::flatten()都会调用Flattener类模板的不同专业化的静态方法。

答案 1 :(得分:2)

你的方法对我来说似乎相当复杂。所以,让我分享我的解决方案,我希望它适合你。如果您真的想要更正代码,而不是解决问题,那么这不是合适的答案。 我共享的所有代码都已经使用visual 2015进行了3级递归测试,但它适用于任何具有任何递归级别的编译器。

让我们从第一个问题开始吧。您的解决方案需要提供数据类型,但该类型已知。那么如何自动恢复呢?我们将使用这个结构:

template <typename T>
struct NestedListType {
  using type = T;
};

给定一个类型,它只给出相同的类型。要在列表中提供类型,我们需要专门化它:

template <typename T>
struct NestedListType<std::list<T> > {
  using type = typename NestedListType<T>::type;
};

现在,如果我们的类型是一个列表,它将给出value_type的{​​{1}},并递归,直到该类型不是std::list。因此,它可以使用多个std::list层。

以这种方式使用:

std::list

接下来我们需要定义我们的功能。我们想要一个将嵌套类型的每个列表放在一个大列表中的函数。为了避免不必要的副本,我们也会在函数的参数中传递结果列表,因此我们需要第一个函数来执行此操作:

using DataType = typename NestedListType<std::list<std::list<int>>>::type; // DataType will be int

最后我们需要做实际的工作。我们需要的是两个函数:一个接收列表并分配每个列表,另一个接收列表并将其添加到我们的结果中。为此,我们将简单地使用重载,因为我们不能(并且不需要)部分地专门化函数:

// if you do not want to modify the original list, this is here where you should remove the reference and make a copy
// T could be itself an std::list, no problem with that
template <typename T>
std::list<typename NestedListType<T>::type> flatten(std::list<T> & list) {
  // obtain a std::list with the appropriate type
  std::list<typename NestedListType<T>::type> result;
  flatten(list, result);
  return result;
}

就是这样!现在,您只需将它与所需的列表和子列表一起使用即可:

template <typename T, typename V>
void flatten(std::list<std::list<T> > & list, std::list<V> & result) {
  // if you want to change the order or the resulting list, change here
  // here simply add the first list first and continue until the last
  for (auto & sublist : list)
    flatten(sublist, result);
}

template <typename T, typename V>
void flatten(std::list<T> & list, std::list<V> & result) {
  // add the list to our result using references and splice to avoid unecessary copies or move
  result.splice(result.end(), list);
}

希望它有所帮助! 如果有什么不清楚,请告诉我,我会尝试更好地解释它。

答案 2 :(得分:1)

首先应该读取错误消息:

  

没有已知的从'std :: list'到参数1的转换   “的std ::列表&LT;的std ::列表&LT; int&gt; &GT;”在静态成员函数'静态   std :: list Flattener :: flatten(std :: list&lt; Ty&gt;)[with T = int;   Ty = std :: list&lt; int&gt;]':

Buggy编译器?还是糟糕的粘贴?

这导致得出的结论是,您希望其他Flattener被调用为:

template<typename T, typename Ty>
struct Flattener{
    static std::list<T> flatten(typename std::list<Ty> deepList){
        struct LAMBDA{
            static Ty flatten_lambda(Ty ty, Ty accum){
                return append(ty, accum);
            }
        };
        Ty ty;
        Ty flat = foldr( static_cast<typename Fold<Ty, Ty>::func>(&LAMBDA::flatten_lambda), ty, deepList);
        return Flattener<T,T>::flatten(flat);
    }
};

请注意额外的<T,T>

答案 3 :(得分:1)

declytype可以节省一天。

template<typename T>
list<T> _flattenOneLevel(list<list<T>> listOfTList) {
    auto lambda = [](list<T> tList, list<T> accumList) {
        return reverse(tList, accumList);
    };

    return reverse(foldl(lambda, empty_list<T>(), listOfTList));
}

template<typename T, typename _DeepList>
struct _Flattener {
    static list<T> flatten(_DeepList deepList) {

        auto oneLevelDown = _flattenOneLevel(deepList);
        return _Flattener<T, decltype(oneLevelDown)>::flatten(oneLevelDown);
    }
};

template<typename T>
struct _Flattener<T, list<T>> {
    static list<T> flatten(list<T> list) {
        return list;
    }
};

template<typename T, typename _DeepList>
list<T> flatten(_DeepList deepList) {
    return _Flattener<T, _DeepList>::flatten(deepList);
}

详细了解递归模板中的类型扣除:What are some uses of decltype(auto)?