как указать вызываемую концепцию С++, когда выводятся типы вызываемых параметров

Я вызываю шаблонную лямбду из шаблонной функции, выводится тип параметров лямбды. Если тип лямбда-авто, то работает: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;
}

я хотел использовать концепция специализации параметр , чтобы быть чем угодно, кроме вызываемого объекта, который принимает как параметр.

Используя 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, но не с ограничивающей концепцией.

Как правильно использовать понятие в этой ситуации?

Это упрощенная версия кода, на самом деле подпись является и размер массива рассчитывается на основе типов пакетов параметров.

============ РЕДАКТИРОВАТЬ 03/03/2022 ==================

Спасибо за правильные ответы, но я упростил код и вопрос, поэтому получаю правильный ответ на неправильный вопрос.

Вам нужно больше контекста, я работаю с микроконтроллерами и хочу создать функцию, которая абстрагирует какую-то транзакцию spi,i2c,modbus и т. д., где один буфер отправляет на подчиненное периферийное устройство и получает буфер в ответ. Функция вычисляет длину буфера записи и чтения, сериализует (при необходимости выполняет преобразование порядка следования байтов), вызывает лямбду для выполнения фактической транзакции в зависимости от транспортного механизма, десериализует и возвращает. Таким образом, длина буферов не может быть рассчитана с помощью (sizeof(Ts) + ...), как это предлагается.

Я сделал более реалистичный пример:живой пример

      
// 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;
}

Вопрос остается прежним: есть ли способ добавить ограничение на параметр auto fun функции testf2?

3 ответа

Понятия (и обязательные предложения в целом) не участвуют в выводе аргументов шаблона. Поскольку ваш Sв данном случае просто sizeof(T), вы должны использовать это.

размер S - сумма всех размеров типов пакета параметров

Тогда сделай это sizeof(Args) + ....

Вы можете использовать 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...))>;

Демо

Никол Болас помогает мне найти решение, заключающееся в том, чтобы создать функцию constexpr без аргументов , которая вычисляет размер, и указать точный тип вызываемого объекта с помощью std::function вместо того, чтобы пытаться специализировать auto с концепцией invocable.

      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);
}

демо

Другие вопросы по тегам