2

我从模板函数调用模板 lambda,导出 lambda 参数类型。如果 lambda 的类型为 auto,则它可以工作: https ://godbolt.org/z/WYxj5G8vx

#include <iostream>
#include <cstdint>
#include <array>
#include <functional>
#include <numeric>
#include <concepts>


template <typename T>
int testf2(T, auto fun) {
  std::array<std::uint8_t, sizeof(T)> ar{};
  std::iota(ar.begin(), ar.end(), 0);
  return fun(ar);
}


int main() { 
    auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int  {
       return arr[S -1];
   };

   std::cout << "R = " << testf2(5, f2) << std::endl;
}

我想使用std::invocable概念来专门化 , 的auto fun参数testf2,而不是std::array<std::uint8_t, N>作为参数的可调用对象。

当我尝试使用 gcc11.2 或 clang13

template <typename T, size_t S>
int testf2(T, std::invocable<std::array<uint8_t, S>> auto fun) {
  std::array<std::uint8_t, sizeof(T)> ar{};
  std::iota(ar.begin(), ar.end(), 0);
  return fun(ar);
}

我得到错误:

候选模板被忽略:无法推断模板参数 'S' int testf2(T, std::invocable<std::array<uint8_t, S>> auto fun) {

我不明白为什么编译器可以在仅使用 auto 时推断类型,但不能使用约束概念。

在这种情况下使用概念的正确方法是什么?

这是代码的简化版本,实际上是的签名testf2testf2(auto fun, ARGS... args)数组的大小是根据参数包类型计算的。

============ 编辑 03/03/2022 ==================

感谢您的正确答案,但我过度简化了代码和问题,所以我得到了错误问题的正确答案。

您需要更多上下文,我使用 MCU,并且想要创建一个函数来抽象某种 spi、i2c、modbus 等事务,其中一个将缓冲区发送到从外设并接收缓冲区作为响应。该函数计算写入和读取缓冲区长度,序列化(如果需要,进行字节序转换),根据传输机制调用 lambda 来执行实际事务,反序列化并返回。因此,缓冲区长度不能按照建议使用 (sizeof(Ts) + ...) 计算。

我做了一个更现实的例子:live example


// return empty array whose size is the sum of the two arrays given as parameters
template<typename T, std::size_t LL, std::size_t RL>
constexpr std::array<T, LL+RL> join(std::array<T, LL>, std::array<T, RL>)
{
    return std::array<T, LL+RL>{};
}


// return an array of size sizeof(T) if T is arithmetic, otherwise an empty array
template <typename T>
constexpr auto count_ari(T) {
      if constexpr (std::is_arithmetic_v<T>) {
      return std::array<uint8_t, sizeof(T)>{};
  } else {
      return std::array<uint8_t, 0>{};
  }
}

// return empty array whose size is the sum of all parameter which are arithmetic
template <typename HEAD, typename... TAIL>
constexpr auto count_ari(HEAD h, TAIL... tail) {
    return join(count_ari(h), count_ari(tail...));
}

// create a iota filled array whose size is sum of all arithmetic parameters
// call a lambda given in parameter on this array
// return what has done the lambda
// it's here that I want to constrain parameter "auto fun" 
template </*size_t S,*/ typename... ARGS>
int testf2(/*std::invocable<std::array<uint8_t, S>>, */ auto fun, ARGS... args) {
  auto ar = count_ari(args...);
  std::iota(ar.begin(), ar.end(), 1);
  return fun(ar);
}


int main() { 
    auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int  {
       return arr[S -1];
   };

  std::cout << "R = " << testf2(f2, 'a') << std::endl;
  std::cout << "R = " << testf2(f2, 6, 7l, "foobar") << std::endl;
}

问题还是一样:有没有办法在函数 testf2 的 auto fun 参数上添加约束

4

3 回答 3

2

概念(以及一般的 requires 子句)不参与模板参数推导。由于您S在这种情况下是 just sizeof(T),因此您应该使用它。

大小 S 是参数包类型的所有大小的总和

然后做出来sizeof(Args) + ...

于 2022-02-02T18:34:58.713 回答
0

Nicol Bolas 帮助我找到了解决方案,该解决方案是使 constexpr 函数使用计算大小的参数,并使用 std::function 指定可调用的确切类型,而不是尝试使用可调用概念专门化 auto 。

template <typename T>
constexpr size_t sizeof_ari() {
    if constexpr (std::is_arithmetic_v<T>) 
      return sizeof(T);
   else 
      return 0;
}

template <typename... ARGS>
constexpr size_t sizeof_aris() {
    return (sizeof_ari<ARGS>() + ...);
}

// create a iota filled array whose size is sum of all arithmetic parameters
// call a lambda given in parameter on this array
// return what has done the lambda
template <typename... ARGS>
using lambda_param = std::array<uint8_t, sizeof_aris<ARGS...>()>;
template <typename... ARGS>
int testf2(std::function<int(lambda_param<ARGS...>)>  fun, ARGS... args) {
  auto ar = make_buf(args...);
  std::iota(ar.begin(), ar.end(), 1);
  return fun(ar);
}

演示

于 2022-02-03T17:55:13.027 回答
0

您可以使用delcltype(count_ari(args...))以下模板参数来获取结果数组类型std::invocable

template <typename... ARGS>
int testf2(
  std::invocable<decltype(count_ari(std::declval<ARGS>()...))> auto fun, 
  ARGS... args);

或者

template <typename... ARGS>
int testf2(auto fun, ARGS... args)
  requires std::invocable<decltype(fun), decltype(count_ari(args...))>;

演示

于 2022-02-02T18:35:05.690 回答