[英]C++ Multiple sets of variadic function arguments
我正在嘗試在計算密集型應用程序中優化一個非常低級且廣泛使用的功能。 假設我有以下類型:
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。
我會將每個集合作為引用的元組傳遞,您可以使用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);
}
用編譯器設置-O2
發出的程序集:
main:
pxor xmm0, xmm0
sub rsp, 8
addss xmm0, DWORD PTR input[rip+16]
addss xmm0, DWORD PTR input[rip+8]
addss xmm0, DWORD PTR input[rip+36]
addss xmm0, DWORD PTR output[rip+24]
addss xmm0, DWORD PTR output[rip+32]
addss xmm0, DWORD PTR output[rip+4]
call emit(float)
xor eax, eax
add rsp, 8
ret
沒有比這更有效率的了。
我想編寫一個可以調用的函數,例如:
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
(請參見示例),即Elem<T, N>
的T
類型和num
,即Elem<T, N>
的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
}
不確定,我是否知道問題,但是您可以
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");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.