Is there any use case for class inside function after introduction of lambda?
From the wikipedia article about Lambda functions and expressions:
users will often wish to define predicate functions near the place where they make the algorithm function call. The language has only one mechanism for this: the ability to define a class inside of a function. ... classes defined in functions do not permit them to be used in templates
Does this mean that use of nested structure inside function is silently deprecated after C++0x lambda are in place ?
Additionall开发者_如何转开发y, what is the meaning of last line in above paragraph ? I know that nested classes cannot be template
; but that line doesn't mean that.
I'm not sure I understand your confusion, but I'll just state all the facts and let you sort it out. :)
In C++03, this was legal:
#include <iostream>
int main()
{
struct func
{
void operator()(int x) const
{
std::cout << x << std::endl;
}
};
func f; // okay
f(-1); // okay
for (std::size_t i = 0; i < 10; ++i)
f(i) ; // okay
}
But if we tried doing this, it wasn't:
template <typename Func>
void exec(Func f)
{
f(1337);
}
int main()
{
// ...
exec(func); // not okay, local classes not usable as template argument
}
That left us with an issue: we want to define predicates to use for this function, but we can't put it in the function. So we had to move it to whatever outer scope there was and use it there. Not only did that clutters that scope with stuff nobody else needed to know about, but it moved the predicate away from where it's used, making it tougher to read the code.
It could still be useful, for the occasional reused chunk of code within the function (for example, in the loop above; you could have the function predicate to some complex thing with its argument), but most of the time we wanted to use them in templates.
C++0x changes the rules to allow the above code to work. They additionally added lambdas: syntax for creating function objects as expressions, like so:
int main()
{
// same function as above, more succinct
auto func = [](int x){ std::cout << x << std::endl; };
// ...
}
This is exactly like above, but simpler. So do we still have any use for "real" local classes? Sure. Lambda's fall short of full functionality, after all:
#include <iostream>
template <typename Func>
void exec(Func func)
{
func(1337);
}
int main()
{
struct func
{
// note: not possible in C++0x lambdas
void operator()(const char* str) const
{
std::cout << str << std::endl;
}
void operator()(int val) const
{
std::cout << val << std::endl;
}
};
func f; // okay
f("a string, ints next"); // okay
for (std::size_t i = 0; i < 10; ++i)
f(i) ; // okay
exec(f); // okay
}
That said, with lambda's you probably won't see local classes any more than before, but for completely different reasons: one is nearly useless, the other is nearly superseded.
Is there any use case for class inside function after introduction of lambda ?
Definitely. Having a class inside a function is about:
- localising it as a private implementation detail of the code intending to use it,
- preventing other code using and becoming dependent on it,
- being independent of the outer namespace.
Obviously there's a threshold where having a large class inside a function harms readability and obfuscates the flow of the function itself - for most developers and situations, that threshold is very low. With a large class, even though only one function is intended to use it, it may be cleaner to put both into a separate source file. But, it's all just tuning to taste.
You can think of this as the inverse of having private functions in a class: in that situation, the outer API is the class's public interface, with the function kept private. In this situation, the function is using a class as a private implementation detail, and the latter is also kept private. C++ is a multi-paradigm language, and appropriately gives such flexibility in modelling the hierarchy of program organisation and API exposure.
Examples:
- a function deals with some external data (think file, network, shared memory...) and wishes to use a class to represent the binary data layout during I/O; it may decide to make that class local if it only has a few fields and is of no use to other functions
- a function wants to group a few items and allocate an array of them in support of the internal calculations it does to derive its return value; it may create a simple struct to wrap them up.
- a class is given a nasty bitwise enum, or perhaps wants to reinterpret a
float
ordouble
for access to the mantisa/exponent/sign, and decides internally to model the value using astruct
with suitable-width bitfields for convenience (note: implementation defined behaviours)
classes defined in functions do not permit them to be used in templates
I think you commented that someone else's answer had explained this, but anyway...
void f()
{
struct X { };
std::vector<X> xs; // NOPE, X is local
}
Defining structures inside functions was never a particularly good way to deal with the lack of predicates. It works if you have a virtual base, but it's still a pretty ugly way to deal with things. It might look a bit like this:
struct virtual_base {
virtual void operator()() = 0;
};
void foo() {
struct impl : public virtual_base {
void operator()() { /* ... */ }
};
register_callback(new impl);
}
You can still continue to use these classes-inside-functions if you want of course - they're not deprecated or crippled; they were simply restricted from the very start. For example, this code is illegal in versions of C++ prior to C++0x:
void foo() {
struct x { /* ... */ };
std::vector<x> y; // illegal; x is a class defined in a function
boost::function<void()> z = x(); // illegal; x is used to instantiate a templated constructor of boost::function
}
This kind of usage was actually made legal in C++0x, so if anything the usefulness of inner classes has actually be expanded. It's still not really a nice way of doing things most of the time though.
Boost.Variant.
Lambdas don't work with variants, as variants need objects that have more than one operator() (or that have a templated operator()). C++0x allows local classes to be used in templates now, so boost::apply_variant
can take them.
As Tony mentioned, a class inside a function is not only about predicates. Besides other use cases, it allows to create a factory function that creates objects confirming to an interface without exposing the implementing class. See this example:
#include <iostream>
/* I think i found this "trick" in [Alexandrescu, Modern C++ Design] */
class MyInterface {
public:
virtual void doSomethingUseful() = 0;
};
MyInterface* factory() {
class HiddenImplementation : public MyInterface {
void doSomethingUseful () {
std::cout << "Hello, World!" << std::endl;
}
};
return new HiddenImplementation();
}
int main () {
auto someInstance = factory();
someInstance->doSomethingUseful();
}
精彩评论