代码之家  ›  专栏  ›  技术社区  ›  TCSGrad

C++14:如何按模板参数对可变输入进行分组?

  •  4
  • TCSGrad  · 技术社区  · 7 年前

    假设我有两个班:

    template <unsigned N>
    class Pixel {
        float color[N];
    public:
        Pixel(const std::initializer_list<float> &il)
        {
          // Assume this code can create a Pixel object from exactly N floats, and would throw a compiler error otherwise
    
        }
    };
    
    template <unsigned N>
    class PixelContainer {
        std::vector<Pixel<N>> container;
    };
    

    我要做的是为 PixelContainer 以便: 它将为以下情况正确实例化(示例,并非详尽无遗):

    PixelContainer<3> pc1(1, 2, 3)          // Creates a container containing one Pixel<3> objects
    PixelContainer<3> pc2(1, 2, 3, 4, 5, 6) // Creates a container containing 2 Pixel<3> objects
    PixelContainer<2> pc3(1, 2, 3, 4, 5, 6) // Creates a container containing 3 Pixel<2> objects
    

    它不会针对以下情况进行编译(例如,并非详尽无遗):

    PixelContainer<3> pc4(2, 3) // Not enough arguments
    PixelContainer<2> pc5(1, 2, 3, 4, 5) // Too many arguments
    

    想自己分组吗

    PixelContainer<2> pc2({1, 2}, {3, 4}, {5, 6}) // Creates a container containing 3 Pixel<2> objects
    

    question 为我背后的灵感)

    4 回复  |  直到 7 年前
        1
  •  4
  •   Yakk - Adam Nevraumont    7 年前
    template<class T, std::size_t I, std::size_t...Offs, class Tup>
    T create( std::index_sequence<Offs...>, Tup&& tup ) {
      return T( std::get<I+Offs>(std::forward<Tup>(tup))... );
    }
    
    template <unsigned N>
    struct Pixel {
        float color[N];
    
        template<class...Ts,
            std::enable_if_t< sizeof...(Ts)==N, bool > = true
        >
        Pixel(Ts&&...ts):color{ std::forward<Ts>(ts)... } {};
    };
    
    template <unsigned N>
    struct PixelContainer {
    
        std::vector<Pixel<N>> container;
        template<class T0, class...Ts,
          std::enable_if_t<!std::is_same<std::decay_t<T0>, PixelContainer>{}, bool> =true
        >
        PixelContainer(T0&& t0, Ts&&...ts):
          PixelContainer( std::make_index_sequence<(1+sizeof...(Ts))/N>{}, std::forward_as_tuple( std::forward<T0>(t0), std::forward<Ts>(ts)... ) )
        {}
        PixelContainer() = default;
    private:
      template<class...Ts, std::size_t...Is>
      PixelContainer( std::index_sequence<Is...>, std::tuple<Ts&&...>&& ts ):
        container{ create<Pixel<N>, Is*N>( std::make_index_sequence<N>{}, std::move(ts) )... }
      {}
    };
    

    create

    然后从(起始索引)+(每个偏移量)创建类型并返回它。

    PixelContainer . 对于元素位于 tuple .

    {} 封闭的ctor container .

    enable_if_t 避免吞咽的东西应该去复印。

    Live example .

      std::enable_if_t<0 == ((sizeof...(Ts)+1)%N), bool> =true
    

    可能是一个有用的补充 的公共ctor。

    没有它,我们只需舍入并丢弃传递给的“额外”元素 . 有了它,如果我们有额外的元素(即,不是N的倍数),我们就得到一个“找不到ctor”。

        2
  •  3
  •   asu    7 年前

    它使用临时 std::array S temp temp_pixels 用于从中复制像素数据 临时雇员 . 最后 container

    我相信那些阵列 走开,但我不确定。看看godbolt,他们似乎是,但我不善于阅读编译器汇编输出:)

    #include <array>
    #include <algorithm>
    #include <cstddef>
    #include <vector>
    
    template <unsigned N>
    struct Pixel {
        float color[N]; // consider std::array here
    };
    
    template <unsigned N>
    class PixelContainer {
        std::vector<Pixel<N>> container;
    
    public:
        template<class... Ts>
        PixelContainer(Ts... values)
        {
            static_assert(sizeof...(Ts) % N == 0, "Pixels should be grouped by 3 values in PixelContainer constructor");
            const std::array<float, sizeof...(Ts)> temp{float(values)...};
            std::array<Pixel<N>, sizeof...(Ts) / N> temp_pixels{};
    
            for (std::size_t i = 0; i < sizeof...(Ts); i += N)
            {
                auto& pixel = temp_pixels[i / N];
    
                std::copy(
                    temp.begin() + i, temp.begin() + i + N,
                    pixel.color
                );
            }
    
            container = std::vector<Pixel<N>>(temp_pixels.begin(), temp_pixels.end());
        }
    };
    
    int main()
    {
        PixelContainer<3> pc1(1, 2, 3);          // Creates a container containing one Pixel<3> objects
        PixelContainer<3> pc2(1, 2, 3, 4, 5, 6); // Creates a container containing 2 Pixel<3> objects
        PixelContainer<2> pc3(1, 2, 3, 4, 5, 6); // Creates a container containing 3 Pixel<2> objects
    
    /*
        PixelContainer<3> pc4(2, 3); // Not enough arguments
        PixelContainer<2> pc5(1, 2, 3, 4, 5); // Too many arguments
    */
    }
    
        3
  •  1
  •   OznOg    7 年前

    我会建议一些混合版本,仍然有一个临时数组,但没有临时的像素

    template <unsigned N>
    struct PixelContainer {
    
        template<std::size_t S, std::size_t... elts>
        auto createOne(const std::array<float, S> &arr, std::size_t offset,
                       std::index_sequence<elts...>) {
            return Pixel<N>{ arr[offset + elts]... };
        }
    
        template<typename... Floats>
        PixelContainer(Floats... vals) {
            static_assert(sizeof...(vals) % N == 0, "invalid number of args");
            std::array<float, sizeof...(vals)> arr = { float(vals)... };
            for (size_t i = 0; i < sizeof...(vals) / N; i++) {
                container.push_back(createOne(arr, i * N, std::make_index_sequence<N>{}));
            }
    
        }
        std::vector<Pixel<N>> container;
    };
    
        4
  •  0
  •   Henning Koehler    7 年前

    我想答案就在你提供的链接里( C++ number of function's parameters fixed by template parameter ). 您只需要在那里更改断言:而不是 sizeof...(Floats) == N sizeof...(Floats) % N == 0 .