簡體   English   中英

C ++多套可變參數函數集

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM