У меня есть класс Foo
, который принимает различные варианты предикатов через свой конструктор.Передача различных шаблонов функции lambdas в C++
template<typename T>
struct Value
{
T value;
};
class Foo
{
public:
template<typename T>
Foo(Value<T> &value, function<bool()> predicate)
{
}
template<typename T>
Foo(Value<T> &value, function<bool(const Value<T> &)> predicate) :
Foo(value, function<bool()>([&value, predicate](){ return predicate(value); }))
{
}
};
Это позволяет мне строить класс с явным function
объекта:
Value<int> i;
Foo foo0(i, function<bool()>([]() { return true; }));
Foo foo1(i, function<bool(const Value<int> &)>([](const auto &) { return true; }));
однако он терпит неудачу, когда пытаются использовать лямбда непосредственно:
Foo fooL1(i, [](const Value<int> &) { return true; });
По причине я не» t понять, однако компилятор не считает наличие неявного преобразования из лямбда в function
в шаблон конструктора. Сообщение об ошибке (Visual C++ 2015, Update 3):
ошибка C2664: 'Foo :: Foo (Foo & &)': не удается преобразовать аргумент 2 из 'главного :: < lambda_f1d2143f356d549800fb3412d8bc61a2>' в 'станд :: функция < BOOL (аннулируются)>'
Теперь я могу добавить еще один шаблон конструктора для лямбды
template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool(const Value<T> &)>(predicate))
{
}
, который будет работать хорошо до тех пор, пока лямбда передается этот конструктор имеет один параметр Value<T>
, однако, естественно, не выполняется для лямбды без параметра:
Foo fooL0(i, []() { return true; });
Так что я, вероятно, потребуется некоторое SFINAE магию, чтобы включить соответствующий шаблон конструктора для разные лямбды, что-то вроде:
template<typename T, typename UnaryPredicate,
typename = enable_if_t<is_callable_without_args> >
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool()>(predicate))
{
}
template<typename T, typename UnaryPredicate,
typename = enable_if_t<is_callable_with_one_arg> >
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool(const Value<T> &)>(predicate))
{
}
Или, может быть, только один шаблон конструктор может сделать трюк, что-то вроде:
template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<???decltype(UnaryPredicate)???>(predicate))
{
}
Или, может быть, совсем другое решение? Вопрос заключается в том, как включить перегрузки конструктора для работы с соответствующими lambdas.
Отлично, хотя я не вижу позади волшебства, он работает как шарм. Можете ли вы в конце концов указать мне какую-либо информацию, описывающую эту технику в читаемой человеком форме? Благодарю. – manison
@manisin block deducton принимает тип и возвращает тот же тип. Но возвращаемый тип больше не выводим. Выделение типа шаблона - это соответствие шаблонов, а 'tag_t :: type' не может быть сопоставлен с шаблоном. Короче, потому что стандарт говорит так. Почему это так? Теоретически ':: type' может быть не связан с' T', на самом деле это 'T'. C++ не требует от системы вывода типов инвертировать произвольные Turing-процессы, а 'tag_t :: type' может иметь произвольное отображение из' T' в ':: type'. –
Yakk