Ответ 1
Ну, этот код будет компилироваться, но проблема в том, что вы не сможете по умолчанию построить любой объект этого класса 1 потому что конструктор лямбда недоступен (кроме копии /move ). Единственными конструкторами, гарантируемыми типом lambda
, является конструктор copy/move по умолчанию. И нет конструктора по умолчанию
Тип замыкания, связанный с лямбда-выражением, не имеет значения по умолчанию конструктор и оператор назначения удаленной копии. Он имеет дефолт конструктор копирования и конструктор перемещения по умолчанию ([class.copy]). [ Примечание. Эти специальные функции-члены неявно определяются как обычно, и поэтому может быть определен как удаленный. - конечная нота]
или cppreference:
//ClosureType() = delete; //(until C++14)
ClosureType(const ClosureType& ) = default; //(since C++14)
ClosureType(ClosureType&& ) = default; //(since C++14)
История конструктора лямбда, которая недоступна, восходит к предложению ранних дней, найдена здесь
В разделе 3, второй абзац, и я цитирую:
В этом переводе
__some_unique_name
- новое имя, не используется в другом месте программы таким образом, чтобы вызвать конфликты с ее использовать как тип замыкания. Это имя и конструктор для класса, не нужно подвергать пользователя - единственные функции, которые пользователь могут полагаться в типе замыкания, являются конструктором копирования (и перемещением конструктор, если это предложение одобрено) и вызов функции оператор. Для типов Closure не нужны конструкторы по умолчанию, назначение операторы или любые другие средства доступа, помимо вызовов функций. Это может быть целесообразным для возможности запретить создание производных классов от типов закрытия....
Как вы можете видеть, предложение даже предполагало, что создание производных классов из типов закрытия должно быть запрещено.
1 Конечно, вы можете скопировать-инициализировать базовый класс с помощью a
, чтобы инициализировать объект типа B
. См. this
Теперь, на ваш вопрос:
Может ли это быть полезным в любом случае?
Не в вашей точной форме. Ваш экземпляр будет доступен только с экземпляром a
.
Однако, если вы наследуете от общего класса Callable, такого как лямбда-тип, есть два случая, о которых я могу думать.
-
Создать функтор, который вызывает группу функторов в заданной последовательности наследования:
Упрощенный пример:
template<typename TFirst, typename... TRemaining> class FunctionSequence : public TFirst, FunctionSequence<TRemaining...> { public: FunctionSequence(TFirst first, TRemaining... remaining) : TFirst(first), FunctionSequence<TRemaining...>(remaining...) {} template<typename... Args> decltype(auto) operator () (Args&&... args){ return FunctionSequence<TRemaining...>::operator() ( TFirst::operator()(std::forward<Arg>(args)...) ); } }; template<typename T> class FunctionSequence<T> : public T { public: FunctionSequence(T t) : T(t) {} using T::operator(); }; template<typename... T> auto make_functionSequence(T... t){ return FunctionSequence<T...>(t...); }
пример использования:
int main(){ //note: these lambda functions are bug ridden. Its just for simplicity here. //For correct version, see the one on coliru, read on. auto trimLeft = [](std::string& str) -> std::string& { str.erase(0, str.find_first_not_of(' ')); return str; }; auto trimRight = [](std::string& str) -> std::string& { str.erase(str.find_last_not_of(' ')+1); return str; }; auto capitalize = [](std::string& str) -> std::string& { for(auto& x : str) x = std::toupper(x); return str; }; auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize); std::string str = " what a Hullabaloo "; std::cout << "Before TrimAndCapitalize: str = \"" << str << "\"\n"; trimAndCapitalize(str); std::cout << "After TrimAndCapitalize: str = \"" << str << "\"\n"; return 0; }
Выход
Before TrimAndCapitalize: str = " what a Hullabaloo " After TrimAndCapitalize: str = "WHAT A HULLABALOO"
Смотрите Live on Coliru
-
Создайте Functor с перегруженным
operator()(...)
, перегруженным всеми базовыми классами <<29 > :- Нир Фридман уже дал хороший пример этому в своем ответе на этот вопрос.
- Я также разработал аналогичный и упрощенный пример, взятый из Его. Смотрите на Coliru
- Джейсон Лукас также продемонстрировал свои практические применения в своей презентации CppCon 2014 "Полиморфизм с Союзами" . Здесь вы можете найти Repo , одно из точного местоположения в исходном коде здесь (Спасибо Cameron DaCamara)
Еще один классный трюк: поскольку результирующий тип из make_functionSequence(...)
является вызываемым классом. Вы можете добавить больше лямбда или вызывать ее позже.
//.... As previously seen
auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize);
auto replace = [](std::string& str) -> std::string& { str.replace(0, 4, "Whaaaaat"); return str; };
//Add more Functors/lambdas to the original trimAndCapitalize
auto replaced = make_functionSequence(trimAndCapitalize, replace /*, ... */);
replaced(str2);