Концепция, которую вы ищете, заключается в том, чтобы f1
был SFINAE дружественным .Это требует, чтобы author из f1
предпринял некоторые действия, чтобы гарантировать, что user имеет какой-то способ обнаружить, что вызов f1
будет плохо сформирован, что приведет кмягкая ошибка.Если f1
написано не для дружественного SFINAE, то обходного пути нет.
Чтобы сделать дружественным f1
SFINAE, мы должны убедиться, что перед некоторой ошибкой компиляции, которая возникнет при создании экземпляра тело из f1
достигается, во-первых, условие, которое вызвало бы эту ошибку, делает подпись из f1
недействительной, так что, когда окружающая инстанциация пытается вызвать или принять адрес f1
, SFINAE включается, чтобы удалить f1
из набора перегрузки, поскольку ошибка возникла в непосредственном контексте создания экземпляра подписи f1
.
Другими словами, в этом случае, поскольку мы считаем, чтосоздание экземпляра вызова f2(t)
в теле f1
может вызвать ошибку, мы должны продублировать этот вызов в подписи f1
.Например, мы могли бы сделать это следующим образом:
template <typename T>
auto f1(T t, std::enable_if_t<...>* = 0) -> decltype(f2(t)) { // actually you may want to decay the type but w/e
return f2(t);
}
Итак, создание экземпляра f1(std::declval<T>())
запускает процесс замены и вычета для f1
, который запускает процесс замены и вычета для f2
.В этот момент, благодаря enable_if
, происходит ошибка замены в сигнатуре f2
, что находится в непосредственном контексте f2
создания экземпляра, поэтому шаблон f2
удаляется из набора перегрузки.В результате вызов f2
в сигнатуре f1
должен быть разрешен из пустого набора перегрузки, что означает, что сбой разрешения перегрузки находится в непосредственном контексте экземпляра f1
.Наконец, это также удаляет шаблон f1
из набора перегрузки, что снова приводит к сбою разрешения перегрузки из-за пустого набора перегрузки, на этот раз в непосредственном контексте реализации is_f1_invocable
, что нам и нужно.
Аналогичным образом, если что-то может пойти не так при создании экземпляра тела f2
, тогда нам нужно изменить сигнатуру f2
, чтобы учесть эту возможность, а также обеспечить распространение SFINAE аналогичным образом.
Конечно, вы должны решить, как далеко вы хотите это сделать.В какой-то момент вы можете решить, что вы действительно хотите вызвать серьезную ошибку на этом этапе, вместо того, чтобы просто удалить сигнатуру из набора перегрузки, передав мягкую ошибку в прилагаемую реализацию.