Well, that code will compile, but the problem is that you will be unable to default construct any object of that class1, because the constructor of the lambda isn't accessible (other than copy/move constructors). The only constructors guaranteed by a lambda
type is a defaulted copy/move constructor. And there's no default constructor
[expr.prim.lambda/21]
The closure type associated with a lambda-expression has no default
constructor and a deleted copy assignment operator. It has a defaulted
copy constructor and a defaulted move constructor ([class.copy]). [
Note: These special member functions are implicitly defined as usual,
and might therefore be defined as deleted. — end note ]
or from cppreference:
//ClosureType() = delete; //(until C++14)
ClosureType(const ClosureType& ) = default; //(since C++14)
ClosureType(ClosureType&& ) = default; //(since C++14)
The history of the lambda constructor being inaccessible dates back to its early days proposal, found here
In section 3, second paragraph, and I quote:
In this translation, __some_unique_name
is a new name, not used
elsewhere in the program in a way that would cause conflicts with its
use as a closure type. This name, and the constructor for the class,
do not need to be exposed to the user—the only features that the user
can rely on in the closure type are a copy constructor (and a move
constructor if that proposal is approved) and the function call
operator. Closure types do not need default constructors, assignment
operators, or any other means of access beyond function calls. It may
be worthwhile for implementability to forbid creating derived classes
from closure types. ...
As you can see, the proposal even suggested that creating derived classes from closure types should be forbidden.
1 of course you can copy-initialize the base class with a
in order to initialize an object of type B
. See this
Now, to your question:
Can this be useful in any way?
Not in your exact form. Your's will only be instantiable with the instance a
.
However, if you inherit from a generic Callable Class such as a lambda type, there are two cases I can think of.
Create a Functor that calls a group of functors in a given inheritance sequence:
A simplified example:
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...);
}
example usage:
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 << ""
";
trimAndCapitalize(str);
std::cout << "After TrimAndCapitalize: str = "" << str << ""
";
return 0;
}
output
Before TrimAndCapitalize: str = " what a Hullabaloo "
After TrimAndCapitalize: str = "WHAT A HULLABALOO"
See it Live on Coliru
Create a Functor with an overloaded operator()(...)
, overloaded with all base classes' operator()(...)
:
- Nir Friedman has already given a good instance of that in his answer to this question.
- I have also drafted out a similar and simplified example, drawn from His. See it on Coliru
- Jason Lucas also demonstrated its practical applications in his CppCon 2014 presentation "Polymorphism with Unions". You can find the Repo here, one of exact location in source code here (Thanks Cameron DaCamara)
Another cool trick: Since the resulting type from make_functionSequence(...)
is a callable class. You can append more lambda's or callable to it at a later time.
//.... 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);