Подтвердить что ты не робот

Как мы можем проверить, можно ли вызывать выражение определенного типа с помощью prvalue?

С у нас есть fancy new is_invocable и новые новые значения, которые на самом деле не являются значениями.

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

У меня возникла проблема, когда использование std::is_invocable для проверки, если вы можете что-то вызывать, и правила prvalue, похоже, сталкиваются:

struct no_move {
  no_move(no_move&&)=delete;
  explicit no_move(int) {}
};
void f( no_move ) {}

теперь мы можем спросить, можно ли вызвать f, используя prvalue типа no_move?

f( no_move(1) )

std::is_invocable< decltype(&f), no_move > не работает, поскольку использует std::declval<no_move>(), который является значением x, как no_move&&, а не значением класса no_move.

В это было то же, но гарантированное elision делает некоторые функции вызываемыми с xvalue (т.е. "T&&" ), а другие с prvalues ​​типа T.

Есть ли альтернатива, или нам нужно придумать свою собственную черту для обработки этого случая?

(В теоретическом мире, где std::declval<T> вернул T вместо T&&, is_invocable, я считаю, поступил правильно).

4b9b3361

Ответ 1

Есть ли альтернатива, или нам нужно придумать свою собственную черту для обработки этого случая?

Да, вам просто нужно написать свой собственный признак, который не использует declval. Предполагая, что вы std::is_detected лежат (что, как я знаю, вы, безусловно, делаете):

template <typename T> T make();

template <typename F, typename... Args>
using invoke_result_t = decltype(std::declval<F>()(make<Args>()...));
//                               ^^^^^^^^^^^^^     ^^^^^

template <typename F, typename... Args>
using is_invocable = std::is_detected<invoke_result_t, F, Args...>;

Таким образом, std::is_invocable<decltype(f), no_move> - false_type, но is_invocable<decltype(f), no_move)> - true_type.

Я намеренно использую declval<F>() для функции вместо make, чтобы разрешить использование decltype(f) здесь. Действительно, invoke_result_t должен быть более сложным и "делать правильную вещь" для указателей на членов и т.д. Но это, по крайней мере, простое приближение, указывающее на жизнеспособность этого подхода.

Ответ 2

Вы злоупотребляете понятием Invocable. Эта концепция означает не что иное, как способность использовать std::invoke для данной функции и предоставленные аргументы.

Вы не можете сделать std::invoke(f, no_move(1)), так как это вызовет копирование/перемещение пересылаемого аргумента. Невозможно использовать значение prvalue в качестве параметра через переадресованный вызов типа invoke. Вы можете передать prvalue на вызов переадресации, но возможный вызов данной функции получит значение x.

Это хорошая причина избежать использования неподвижных типов в качестве параметров значения в функциях. Возьмите их вместо const&.

С++ не имеет характеристики типа, чтобы увидеть, можно ли вызвать функцию с определенными параметрами так, как вы хотите.