自动生成函数 header,可变参数模板

Autogenerate function header, variadic template

我有一个问题,我定义了一个模板class来应对不同维度的系统如下:

template <std::size_t N>
class system {

    std::array<cv::Mat, N> matrices;

    ...

};

然后我需要定义不同的函数,根据系统的大小采用不同的参数。类似的东西:

template <>
template<typename T>
void system<1>::fun(T & a){ }

template <>
template<typename T>
void system<2>::fun(T & a, T & b){ }

template <>
template<typename T>
void system<3>::fun(T & a, T & b, T & c){ }

然而,尝试使用此策略时,编译器会出现以下错误:

Out-of-line definition of 'fun' does not match any declaration in 'system<3>'

此外,我希望 headers 函数能够根据模板参数 N 自动生成。我尝试使用可变参数模板但没有运气。

一个可能的解决方案是在class的函数体内定义函数(顺带一提:避免名称system():会与标准函数冲突),使用SFINAE,如下

template <std::size_t N>
class systemClass
 {
   private: 
      std::array<FooType, N> matrices;

   public:
      template<typename T, std::size_t M = N>
      typename std::enable_if<M == 1U>::type fun(T & a) { }

      template<typename T, std::size_t M = N>
      typename std::enable_if<M == 2U>::type fun(T & a, T & b) { }

      template<typename T, std::size_t M = N>
      typename std::enable_if<M == 3U>::type fun(T & a, T & b, T & c) { }
 };

Moreover I would like that the headers functions will be autogenerate based on the template parameter N. I tried to use variadic template but without fortune.

我同意 UnholySheep:我不清楚你到底想要什么,但我怀疑解决方案可能是一个 shell 脚本来生成代码。

如果您可以基于 N 自动生成,我想您可以编写代码来完成您通常需要的操作(您尝试使用可变参数的评论强化了这一点)。

不幸的是,您的函数也在 T 上进行了模板化,这一事实使事情变得比我想要的要复杂一些。有比我给出的更简单的解决方案,但我看到的唯一解决方案要求您显式指定类型,或者将检查推迟到运行时,这可以在编译时完成。就目前而言,我能看到做你想做的唯一方法是使用可变参数模板。这得到了你想要的大部分内容:

template <std::size_t N>
class System {

   template <class ... Ts>
   void fun(Ts& ts) {
       static_assert(sizeof...(Ts) == N, "Wrong number of parameters!");       
    }
};

我使用 static asserted 而不是 enable if,以使事情更简单(而且它不太可能产生影响,除非您计划有另一个名为 fun 的成员函数...不要不要那样做)。现在,此函数将只接受使用 N 个参数调用,但它将允许所有类型发生变化。您希望它们都相同。所以我们需要一点TMP。

template <class ... Ts>
struct all_same{};

template <class T>
struct all_same<T> : std::true_type {
    using same_type = T;    
};

template <class T, class ... Ts>
struct all_same<T, T, Ts...> : all_same<T, Ts...> {};

template <class T1, class T2, class ... Ts>
struct all_same<T1, T2, Ts...> : std::false_type {};

一些经典的递归 TMP 可以让我们得到我们想要的。两者都是 true 和 false 指示包中的所有类型是否相同,如果它们相同,我们可以访问通用类型。一旦我们有了一个通用类型,并验证了大小,我们就可以使用 pack 来初始化一个数组并对其进行循环,这样我们就不必在我们的函数中继续进行烦人的可变参数样式编程:

template <std::size_t N>
struct System {

   template <class ... Ts>
   void fun(Ts&... ts) {
       static_assert(sizeof...(Ts) == N, "Wrong number of parameters!");
       using same = all_same<Ts...>;
       static_assert(same::value, "All types must be the same!");
       std::array<std::reference_wrapper<typename same::same_type>, N> x{ts...};
       for (auto& e : x) { std::cerr << e << std::endl; }
    }
};

修改此解决方案以满足您的确切需求将需要一些 C++ 专业知识,而且您还需要注意我们的某些棘手情况,例如当您同时传递字符串文字和 std::strings 或您习惯于隐式转换的其他类型时,它将失败。不过,希望这能帮助你前进。现场示例:http://coliru.stacked-crooked.com/a/08ac23da33deb8ef.

我相信您还可以使用 integer_sequence 和别名模板使 foo 更通用。 (integer_sequence 是 c++14,但也存在 c++11 实现):

#include <utility>
#include <array>

template <class T, std::size_t>
using typer = T;

template <std::size_t N, class = std::make_index_sequence<N>>
struct S;

template <std::size_t N, std::size_t... Is>
struct S<N, std::index_sequence<Is...>>{

    std::array<int, N> matrices;

    template <class T>
    void foo(typer<const T&, Is>... args) {
        int dummy[] = { ((matrices[Is] = args), void(), 0)... };
        static_cast<void>(dummy);
    }
};

int main() {
    S<3> s;
    s.foo(1, 2, 3);
}

[live demo]

您可以使您的函数可变,但只接受正确数量的参数。它看起来像这样:

template <std::size_t N>
struct system {
    template<typename... Ts>
    auto fun(Ts&&... ts) -> std::enable_if_t<(N == sizeof...(Ts))> {
        // function content
    }

private:
    std::array<cv::Mat, N> matrices;
};

如果参数数量等于N.

,enable if 将只允许函数存在