可变模板基类调用转移

Variadic template base class call forwarding

本文关键字:调用 转移 基类      更新时间:2023-10-16

在c++版本之前,我有这样的代码:

template<class T,class U,class V>
struct Foo : T,U,V {
  bool init() {
    if(!T::init() || !U::init() || !V::init())
      return false;
    // do local init and return true/false
  }
};

我想将其转换为c++ 11可变语法,以获得灵活长度参数列表的好处。我理解使用递归解包模板参数列表的概念,但我只是看不到语法正确。以下是我尝试过的:

template<typename... Features>
struct Foo : Features... {
  template<typename F,typename... G>
  bool recinit(F& arg,G&& ...args) {
    if(!F::init())
      return false;
    return recinit<F,G...>(args...);
  }
  bool init() {
    // how to call recinit() from here?
  }
};

我更喜欢调用基类init()函数的顺序从左到右,但这并不重要。

应该可以:

template<typename F, typename... T>
    struct recinit;
template<typename F>
    struct recinit<F> {
        static bool tinit(F *) {
            return true;
        }
    };
template<typename F, typename T, typename... G>
    struct recinit<F, T, G...> {
        static bool tinit(F *ptr)  {
            if (!ptr->T::init())
                return false;
            return recinit<F, G...>::tinit(ptr);
        }
    };
template<typename... Features>
struct Foo : Features... {
    bool init() {
        bool res = recinit<Foo, Features...>::tinit(this);
        //use res wisely
    }
};

你的问题是你不能写函数的部分特化,只能写类/结构的部分特化。辅助结构体必须在Foo之外,否则它将从封闭结构体中获取模板参数,这将是糟糕的。

你没有说,但我假设init是一个非静态成员函数。如果是这样的话,args的论点就没有什么意义了:它们都应该是this !跳过这一次,避免在参数中使用包。我试着传递this作为void*,但这可能会很麻烦,所以我只是添加了一个额外的模板参数到recinit,这将是Foo

而且,每次执行一个递归步骤时,请记住删除一个参数

也许你可以试试这样做:

template<typename... Features>
struct Foo : Features...
{
    bool init()
    {
        // Courtesy of Xeo :-)
        auto il = {(static_cast<bool (Foo::*)()>(&Features::init))...};
        return std::all_of(il.begin(), il.end(), 
            [this] (bool (Foo::*f)()) { return (this->*f)(); }
            );
    }
};

这里是另一个使用可变模板的更详细的版本:

template<typename... Features>
struct Foo : Features...
{
    bool init()
    {
        return combine_and((&Features::init)...);
    }
private:
    bool combine_and()
    {
        return true;
    }
    template<typename F>
    bool combine_and(F f)
    {
        return (this->*f)();
    }
    template<typename F1, typename... Fs>
    bool combine_and(F1 f1, Fs... fs)
    {
        return ((this->*f1)() && combine_and(fs...));
    }
};

无论您选择哪种解决方案,都可以这样使用:

#include <iostream>
using namespace std;
struct A { bool init() { cout << "Hello " << endl; return true; } };
struct B { bool init() { cout << "Template " << endl; return true; } };
struct C { bool init() { cout << "World!" << endl; return true; } };
int main()
{
    Foo<A, B, C> f;
    bool res = f.init(); // Prints "Hello Template World!"
    cout << res; // Prints 1
}

您的代码有两个问题。首先,相当普通的:

return recinit<F,G...>(args...);

您已经处理了F,请将其从参数列表中删除。

return recinit<G...>(args...);

(另外,您可能应该完美地转发参数。)

其次,代码不会编译,因为你的递归在运行时有一个锚,而在编译时没有。也就是说,编译器将尝试无限地解压缩参数pack G。为了防止这种情况,你需要专门化函数用于空模板参数列表