【问题标题】:Class with types dependant on variadic templating类型取决于可变参数模板的类
【发布时间】:2018-04-09 22:32:23
【问题描述】:

我最近看了一个视频,启发我编写自己的神经网络系统,我希望网络中的节点数量可以调整。

起初我在运行时通过解析节点数数组来实现这一点,但我想知道是否可以在编译时这样做。这是我希望完成的事情的一个例子。

template<int FirstNodes, int SecondNodes, int... OtherNodes>
class Net
{
    tuple<Eigen::Matrix<float, FirstNodes, SecondNodes>, ...> m_weights;
    // More matricies with the values from the OtherNodes
};

作为一个更详细的例子,Net&lt;784, 16, 16, 10&gt; n;n.m_weight 应该有类型

tuple<Eigen::Matrix<float, 784, 16>,
    Eigen::Matrix<float, 16, 16>,
    Eigen::Matrix<float, 16, 10>>

根据我对 C++ 和 constexpr 的了解,这应该是可能的。

我应该补充一点,我能够做到

template<int FirstNodes, int SecondNodes, int... OtherNodes>
class Net
{
public:
    Net()
    {
        auto nodes = {FirstNodes, SecondNodes, OtherNodes...};

        auto i = nodes.begin();
        do 
        {
            // Eigen::Matrix<float, Dynamic, Dynamic>
            Eigen::MatrixXf m(*(i++), *i);
        } while (i+1 != nodes.end());
    }
};

但是我只是再次使用动态矩阵,这不是我所希望的。

任何建议或工作示例将不胜感激。

【问题讨论】:

  • 顺便说一句,m(*(i++), *i) 行调用了直到 c++14 的未定义行为,在 c++17 中未指定
  • 无耻的自我推销你可能会觉得this repo很有趣

标签: c++ c++11 templates variadic-templates template-meta-programming


【解决方案1】:

您想要某种类型的转换,给定N 整数列表返回tupleN - 1 矩阵。这是一个 C++17 解决方案:

template <int A, int B, int... Is>
auto make_matrix_tuple()
{   
    if constexpr(sizeof...(Is) == 0)
    {
        return std::tuple<Eigen::Matrix<float, A, B>>{};
    }
    else
    {
        return std::tuple_cat(make_matrix_tuple<A, B>(), 
                            make_matrix_tuple<B, Is...>());
    }
}

live example on wandbox


在 C++11 中,您可以递归地实现这种类型转换:

template <int... Is>
struct matrix_tuple_helper;

template <int A, int B, int... Rest>
struct matrix_tuple_helper<A, B, Rest...>
{
    using curr_matrix = Eigen::Matrix<float, A, B>;
    using type = 
        decltype(
            std::tuple_cat(
                std::tuple<curr_matrix>{},
                typename matrix_tuple_helper<B, Rest...>::type{}
            )
        );
};

template <int A, int B>
struct matrix_tuple_helper<A, B>
{
    using curr_matrix = Eigen::Matrix<float, A, B>;
    using type = std::tuple<curr_matrix>;
};

template <int... Is>
using matrix_tuple = typename matrix_tuple_helper<Is...>::type;

C++14 方法:

struct matrix_tuple_maker
{
    template <int A, int B, int C, int... Is>
    static auto get()
    {
        return std::tuple_cat(get<A, B>(), get<B, C, Is...>());
    }

    template <int A, int B>
    static auto get()
    {
        return std::tuple<Eigen::Matrix<float, A, B>>{};
    }
};

static_assert(std::is_same_v<
    decltype(matrix_tuple_maker::get<784, 16, 16, 10>()),
    std::tuple<Eigen::Matrix<float, 784, 16>,
               Eigen::Matrix<float, 16, 16>,
               Eigen::Matrix<float, 16, 10>>
    >);

【讨论】:

    【解决方案2】:

    在我看来,您需要两个与 1 相位不同的整数列表。

    如果你定义了一个普通的模板整数容器(在 C++14 中你可以使用std::integer_sequence

    template <int...>
    struct iList
     { };
    

    你可以如下定义一个基类(对不起:用foo代替Eigen::Matrix

    template <typename, typename, typename = std::tuple<>>
    struct NetBase;
    
    // avoid the first couple
    template <int ... Is, int J0, int ... Js>
    struct NetBase<iList<0, Is...>, iList<J0, Js...>, std::tuple<>>
       : NetBase<iList<Is...>, iList<Js...>, std::tuple<>>
     { };
    
    // intermediate case
    template <int I0, int ... Is, int J0, int ... Js, typename ... Ts>
    struct NetBase<iList<I0, Is...>, iList<J0, Js...>, std::tuple<Ts...>>
       : NetBase<iList<Is...>, iList<Js...>,
                 std::tuple<Ts..., foo<float, I0, J0>>>
     { };
    
    // avoid the last couple and terminate
    template <int I0, typename ... Ts>
    struct NetBase<iList<I0>, iList<0>, std::tuple<Ts...>>
     { using type = std::tuple<Ts...>; };
    

    Net 简单地变成(观察整数列表的异相对)

    template <int F, int S, int... Os>
    struct Net : NetBase<iList<0, F, S, Os...>, iList<F, S, Os..., 0>>
     { };
    

    以下是完整的编译示例

    #include <tuple>
    
    template <int...>
    struct iList
     { };
    
    template <typename, int, int>
    struct foo
     { };
    
    template <typename, typename, typename = std::tuple<>>
    struct NetBase;
    
    // avoid the first couple
    template <int ... Is, int J0, int ... Js>
    struct NetBase<iList<0, Is...>, iList<J0, Js...>, std::tuple<>>
       : NetBase<iList<Is...>, iList<Js...>, std::tuple<>>
     { };
    
    // intermediate case
    template <int I0, int ... Is, int J0, int ... Js, typename ... Ts>
    struct NetBase<iList<I0, Is...>, iList<J0, Js...>, std::tuple<Ts...>>
       : NetBase<iList<Is...>, iList<Js...>,
                 std::tuple<Ts..., foo<float, I0, J0>>>
     { };
    
    // avoid the last couple and terminate
    template <int I0, typename ... Ts>
    struct NetBase<iList<I0>, iList<0>, std::tuple<Ts...>>
     { using type = std::tuple<Ts...>; };
    
    template <int F, int S, int... Os>
    struct Net : NetBase<iList<0, F, S, Os...>, iList<F, S, Os..., 0>>
     { };
    
    int main()
     {
       static_assert(std::is_same<
          typename Net<784, 16, 16, 10>::type, 
          std::tuple<foo<float, 784, 16>, foo<float, 16, 16>,
                     foo<float, 16, 10>>>{}, "!");
     }
    

    【讨论】:

      【解决方案3】:

      这是另一个 C++14 解决方案。我认为它值得发布,因为它是非递归且可读的。

      #include <tuple>
      #include <utility>
      
      template<class, int, int> struct Matrix {};
      
      template<int... matsizes, std::size_t... matinds>
      constexpr auto make_net(
        std::integer_sequence<int, matsizes...>,
        std::index_sequence<matinds...>
      ) {
        constexpr int sizes[] = {matsizes...};
        return std::tuple< Matrix<float, sizes[matinds], sizes[1+matinds]>... >{};
      }
      
      template<int... matsizes>
      constexpr auto make_net(
        std::integer_sequence<int, matsizes...> sizes
      ) {
        static_assert(sizes.size() >= 2, "");
        constexpr auto number_of_mats = sizes.size() - 1;
        return make_net(sizes, std::make_index_sequence<number_of_mats>{});
      }
      
      int main () {
        auto net = make_net(std::integer_sequence<int, 784, 16, 16, 10>{});
        using Net = decltype(net);
      
        static_assert(
          std::is_same<
            std::tuple<
              Matrix<float, 784, 16>,
              Matrix<float, 16, 16>,
              Matrix<float, 16, 10>
            >,
            Net
          >{}, ""
        );
      
        return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-09
        • 2012-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-11
        相关资源
        最近更新 更多