Создание sfinae работает для функций с выведенным типом возврата?
Рассмотрим следующий код:
// -------------------------------------------------------------------------- //
// Preprocessor
#include <array>
#include <vector>
#include <utility>
#include <iostream>
#include <type_traits>
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// Calls a function without arguments if it can be called
template <
class F,
class... Args,
class = decltype(std::declval<F>()())
>
decltype(auto) apply(F&& f)
{
std::cout<<"apply(F&& f)"<<std::endl;
return std::forward<F>(f)();
}
// Calls a function with arguments if it can be called
template <
class F,
class Arg,
class... Args,
class = decltype(std::declval<F>()(
std::declval<Arg>(), std::declval<Args>()...
))
>
decltype(auto) apply(F&& f, Arg&& arg, Args&&... args)
{
std::cout<<"apply(F&& f, Arg&& arg, Args&&... args)"<<std::endl;
return std::forward<F>(f)(
std::forward<Arg>(arg),
std::forward<Args>(args)...
);
}
// Does nothing if the function cannot be called with the given arguments
template <
class F,
class... Args
>
void apply(F&& f, Args&&... args)
{
std::cout<<"apply(F&& f, Args&&... args)"<<std::endl;
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// Main function
int main(int argc, char* argv[])
{
// Initialization
auto f = [](auto&& x) -> decltype(std::forward<decltype(x)>(x).capacity()) {
return std::forward<decltype(x)>(x).capacity();
};
auto g = [](auto&& x) -> decltype(auto) {
return std::forward<decltype(x)>(x).capacity();
};
auto h = [](auto&& x) {
return std::forward<decltype(x)>(x).capacity();
};
// Test
apply(f, std::vector<double>()); // -> sfinae works
apply(g, std::vector<double>()); // -> sfinae works
apply(h, std::vector<double>()); // -> sfinae works
apply(f, std::array<double, 1>());// -> sfinae works
//apply(g, std::array<double, 1>()); -> sfinae fails, does not compile
//apply(h, std::array<double, 1>()); -> sfinae fails, does not compile
// Return
return 0;
}
// -------------------------------------------------------------------------- //
Утилита apply
, применяет функцию к аргументам, когда она может компилироваться, в противном случае она ничего не делает. Механизм опирается на сфину. Однако для функции, тип возвращаемой которой выводится из тела, например g
а также h
в приведенном выше примере sfinae не удается. Был бы разумный способ в C++14 изменить apply
утилита, которая заставляет sfinae срабатывать даже для функций, тип возвращаемых данных которых выводится из тела?
Примечание: я хотел бы сделать g
а также h
работать f
Это означает, что зов apply
следует позвонить void
версия.
2 ответа
SFINAE может только ловить ошибки замещения.
Некоторые ошибки при вызове функции не могут быть ошибками замещения. К ним относятся ошибки, возникающие при разборе тела функции.
C++ явно решил исключить эти ошибки из запуска SFINAE и вместо этого вызвать серьезные ошибки, чтобы освободить компиляторы от необходимости компилировать тела произвольных функций, чтобы определить, возникает ли SFINAE. Поскольку SFINAE необходимо выполнять во время разрешения перегрузки, это упрощает код разрешения перегрузки компилятора C++.
Если вы хотите, чтобы ваш код был дружественным к SFINAE, вы не можете использовать такие лямбды, как g
или же h
,
Ваш SFINAE работает просто отлично, что касается apply
,
Что касается почему f
работает, это потому что:
Это шаблон и
Это терпит неудачу уже в фазе замены.
Это означает, что у него есть собственный SFINAE, и для f
по умолчанию ничего не вызывается (для array
), как предполагалось.
Был бы разумный способ в C++14 изменить утилиту применения так, чтобы она заставляла...
Нет, apply
уже делает все, что должен.
Что касается почему g()
а также h()
не работает, они будут выдавать серьезные ошибки, когда вы вызываете их с чем-то, что не имеет capacity
, Служебная функция, которую вы (пытаетесь) применить после этого, не может удалить эту серьезную ошибку. Единственный способ исправить это - "двойной удар" SFINAE, как вы делаете в f()