我试图在计算密集型应用程序中优化非常低级别和广泛使用的功能。 我们说我有以下类型:
template<typename T, int N>
class Elem {...};
我想编写一个可以调用的函数,例如:
template<typename T, int N>
void func(const Elem<T, N> & ... /*N elements*/, Elem<T, N> & ... /* N elements*/)
我正在寻找一种方法,我可以确保编译器能够删除函数签名引入的任何临时文件。
元素通常是从矢量/数组的不同位置获取的元素。例如。 :
Elem<float, 3> inputs[10];
Elem<float, 3> outputs[10];
...
func(input[4], input[2], input[9], output[6], output[8], output[1]);
答案可能是初始化列表,但我担心它可能会有一些开销......
注意:上面的间接索引都是编译时计算的函数,并且在短范围内。
修改
事实上,我喜欢的是:
template<typename... T, int N>
void func(const Elem<T, N>&... inputs, const Elem<T, N>&... outputs)
{
static_assert(sizeof...(inputs) == N, "invalid number of arguments");
static_assert(sizeof...(outputs) == N, "invalid number of arguments");
static_assert(std::is_same<std::integral_constant<int N>...>::value, "invalid arguments");
}
但我无法在VS2017上编译此代码。 答案可以是C ++ 17。
答案 0 :(得分:3)
我会将每个集合作为引用元组传递,您可以使用std::tie
进行引用。
在大多数情况下,根本没有开销,因为编译器会看到所有元组结构。
示例:
#include <tuple>
#include <type_traits>
template<class T, std::size_t N>
struct Elem {
T value() const { return val; }
T val;
};
Elem<float, 3> input[10];
Elem<float, 3> output[10];
namespace detail {
template<typename T, typename F, std::size_t... Is>
constexpr auto tuple_foreach(T&& tup, F& f, std::index_sequence<Is...>) {
using expand = int[];
void(expand{0,
(f(std::get<Is>(std::forward<T>(tup))), 0)...
})
;
}
}
template<typename T, typename F, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
constexpr auto tuple_foreach(T&& tup, F f) {
return detail::tuple_foreach(
std::forward<T>(tup), f,
std::make_index_sequence<TupSize>{}
);
}
template<class Set1, class Set2>
auto func(Set1 set1, Set2 set2)
{
constexpr auto N1 = std::tuple_size<Set1>::value;
constexpr auto N2 = std::tuple_size<Set2>::value;
static_assert(N1 == N2, "");
// now do things with std::get<0 ... N-1>(set1) and
// std::get<0 ... N-1>(set2);
using result_type = std::decay_t<decltype(std::get<0>(set1).value())>;
// let's compute the sum of the inputs
result_type result = 0;
tuple_foreach(set1,
[&](auto&& elem)
{
result += elem.value();
});
tuple_foreach(set2,
[&](auto&& elem)
{
result += elem.value();
});
return result;
}
void emit(float);
int main()
{
auto x = func(std::tie(input[4], input[2], input[9]),
std::tie(output[6], output[8], output[1]));
emit(x);
}
使用编译器设置[{1}}:
发出的程序集-O2
不能比那更有效率。
答案 1 :(得分:2)
我想编写一个可以调用的函数,例如:
template<typename T, int N>
void func(const Elem<T, N> & ... /*N elements*/,
Elem<T, N> & ... /* N elements*/)
据我所知,你问的内容并不容易用现有语言表达。
我能想象的最好的是编写func()
函数,如下所示
template <typename ... Es>
typename std::enable_if<checkElems<Es...>::value>::type
func (Es & ... es)
{
using type = typename checkElems<Es ...>::type; // former T
constexpr std::size_t num { sizeof...(Es) >> 1 }; // former N
// ...
}
func()
收到参数列表(Es & ... es
)且仅当类型的相对列表(Es ...
)满足实现的要求列表时才启用SFINAE功能自定义类型特征checkElems
(请参阅以下示例)。
以下checkElems
检查:
Elem<T, N> const
2 * N
N
类型彼此相等N
类型彼此相同const
以下N
类型,它们等于第一个N
const
,以下N
类型与第一个N
不同在func()
内,您可以使用type
(参见示例),即T
中的Elem<T, N>
类型和num
,这是N
中的Elem<T, N>
值。
你可以做这个检查,但我不知道这是不是一个好主意。
可编辑的例子
#include <tuple>
#include <type_traits>
template<typename T, std::size_t N>
struct Elem {};
template <typename>
struct extrElem;
template <typename T, std::size_t N>
struct extrElem<Elem<T, N> const>
{
using type = T;
static constexpr std::size_t num { N };
};
template <std::size_t, std::size_t, typename ...>
struct extrTypes;
template <std::size_t Skip, std::size_t Num, typename ... Es, typename T0,
typename ... Ts>
struct extrTypes<Skip, Num, std::tuple<Es...>, T0, Ts...>
{ using type = typename extrTypes<
Skip-1U, Num, std::tuple<Es...>, Ts...>::type; };
template <std::size_t Num, typename ... Es, typename T0, typename ... Ts>
struct extrTypes<0U, Num, std::tuple<Es...>, T0, Ts...>
{ using type = typename extrTypes<
0U, Num-1U, std::tuple<Es..., T0>, Ts...>::type; };
template <typename ... Es, typename T0, typename ... Ts>
struct extrTypes<0U, 0U, std::tuple<Es...>, T0, Ts...>
{ using type = std::tuple<Es...>; };
template <typename ... Es>
struct extrTypes<0U, 0U, std::tuple<Es...>>
{ using type = std::tuple<Es...>; };
template <typename>
struct sameContTypes : public std::false_type
{ };
template <template <typename ...> class C, typename T0, typename ... Ts>
struct sameContTypes<C<T0, Ts...>>
: public std::is_same<C<T0, Ts...>, C<Ts..., T0>>
{ };
template <typename E0, typename ... Es>
struct checkElems
{
static constexpr std::size_t num { extrElem<E0>::num };
using type = typename extrElem<E0>::type;
using lt1 = typename extrTypes<0U, num, std::tuple<>, E0, Es...>::type;
using lt2 = typename extrTypes<num, num, std::tuple<>, E0, Es...>::type;
static constexpr bool value {
( (num << 1) == 1U + sizeof...(Es) )
&& sameContTypes<lt1>::value
&& sameContTypes<lt2>::value
&& (true == std::is_same<E0,
typename std::tuple_element<0U, lt2>::type const>::value)
&& (false == std::is_same<E0,
typename std::tuple_element<0U, lt2>::type>::value) };
};
template <typename ... Es>
typename std::enable_if<checkElems<Es...>::value>::type
func (Es & ... es)
{
using type = typename checkElems<Es ...>::type; // former T
constexpr std::size_t num { sizeof...(Es) >> 1 }; // former N
// ...
}
int main()
{
Elem<int, 3> ei3;
Elem<int, 4> ei4;
Elem<int, 3> const eci3;
Elem<int, 4> const eci4;
func(eci3, eci3, eci3, ei3, ei3, ei3); // compile
//func(eci3, eci3, eci3, ei3, eci3, ei3); // compilation error
//func(eci3, eci3, eci3, ei3, ei3, ei4); // compilation error
//func(eci3, eci3, eci4, ei3, ei3, ei3); // compilation error
//func(eci4, eci4, eci4, ei4, ei4, ei4); // compilation error
//func(eci4, eci4, eci4, eci4, ei4, ei4, ei4); // compilation error
func(eci4, eci4, eci4, eci4, ei4, ei4, ei4, ei4); // compile
}
答案 2 :(得分:0)
不确定,我是否得到了问题,但你可以做到
template<typename... T, int... N>
void func(const Elem<T, N>&... elems)
如果您希望您的函数采用任意数量的Elem<>
。如果您需要将它们限制为单个T
(例如Elem<int,>
并且Elem<float,>
可能无法一起传递),请使用
template<typename T, int... N>
void func(const Elem<T, N>&... elems)
如果您需要限制参数的数量,只需使用
static_assert(sizeof...(elems) <= 6, "...");
在函数体内。如果所有元素的N
必须相等且参数的数量应为2 * N,请使用:
template<typename... T, int N>
void func(const Elem<T, N>&... elems)
{
static_assert(sizeof...(elems) == 2*N, "invalid number of arguments");
}