![](/img/trans.png)
[英]How to: Extend C++14 template function to variadic template, arguments
[英]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.