繁体   English   中英

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

[英]C++14: How to group variadic inputs by template parameter?

说我有两个班:

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

(看到这个问题的背后的灵感)

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的私人ctor中使用PixelContainer 它具有每个像素的索引序列元素,其元素在tuple

我们将索引序列元素乘以N,即每个索引序列的元素数,并将其传递给create。 此外,我们传入索引序列0,...,N-1作为偏移量和主元组。

然后,我们解包成{}用于密闭的构造函数container

公共ctor只使用每个元素一个(等于参数计数/ N)的正确索引包转发给私有ctor。 它有一些SFINAE烦恼enable_if_t东西,以避免它吞下应该复制ctor的东西。

实例

也,

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

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

没有它,我们只需向下舍入并丢弃传递给PixelContainer “额外”元素。 有了它,如果我们有额外的元素(即,不是N的倍数),我们得到“没有发现”。

做了一些东西,它比@ Yakk的答案更依赖编译器优化性能。

它使用临时std::array s。 temp用于在某处存储传递的值。 temp_pixels用于从temp复制像素数据。 最后将temp复制到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
*/
}

我会提出一些混合版本,还有一个临时数组,但没有临时的像素

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;
};

我认为在你提供的链接中给出了答案( 由模板参数修复的C ++函数参数 )。 你只需要在那里更改断言:而不是sizeof...(Floats) == N你需要sizeof...(Floats) % N == 0

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM