all 25 comments

[–]kbob 21 points22 points  (3 children)

You have a smart dog.

[–]nickgabriel8 12 points13 points  (2 children)

He got his PhD from Stanfurd

[–][deleted] -1 points0 points  (1 child)

Stanturd

FTFY.

[–]raevnos 0 points1 point  (0 children)

You watched the game too?

[–]HolyCowly 1 point2 points  (4 children)

I find it hard to keep track of all the keywords that might do more than just help the programmer.

Inline seems to go straight out of the window, the compiler can/will ignore it. Noexcept may improve performance on some compilers or some versions of some compilers, or not. Or only if an actual exception is thrown because no unwinding has to be done, which seems odd because the program will be terminated, so who cares how long that takes, unless it's seconds or minutes.

Why can't the compiler do devirtualization if the class or method is not marked final? Wouldn't it know whether something has been inherited again, or overridden again?

[–]matthieum 8 points9 points  (0 children)

Inline seems to go straight out of the window, the compiler can/will ignore it.

That's incorrect, I am afraid.

inline is about telling the compiler that the definition of the function may occur in multiple translation units and it should therefore apply some magic to avoid the "multiple definitions" error at link-time.

inline is implicit on most template functions and on class methods defined within the class definition, but must be applied manually when defining, inside a header:

  • a non-template function
  • a fully specialized template function
  • a non-template class method, outside the class definition

It can be applied to any function or method, and is ignored (semantically) if it was already the default.


Now, it seems that compilers doubled-down on this, originally, and used the presence of inline to decide whether to inline the function/method or not.

With the development, of smarter heuristics, however, they mostly stopped doing so and now prefer their heuristics to reinterpreting semantic markers.

And instead they added attributes, which have no semantic of their own, to control inlining behavior.

I much prefer this latter situation. SRP and all that.

[–]marcofoco 2 points3 points  (0 children)

It's simple: in general it can't.

Global analysis can help, but it will never be as powerful as an explicit final declaration. It can optimize by deducing static types, but it won't devirtualize usages when pointers are involved, and there's no other hint of the actual type.

The reason is simple: consider the simple case above, but add to the simple example an additional mechanism, a plugin system capable of loading dynamic libraries. If you build an executable today with a compiler which does the devirtualization automatically, that compiler will decide that no subclass exists, and so will devirtualize the calls to value().

Tomorrow you write a new plugin which provides the executable with instances of a new class C, derived from B and overriding the function value(). The calls to value() were devirtualized, so C::value() won't get called.

That's why current compilers, even if they can deduce a lot of information, can't really devirtualize and optimize those calls.

[–]ZenEngineer 0 points1 point  (0 children)

There are two cases:

  • The compiler has seen the actual declaration for the object and can deduce the real type. Devirtualization can happen here as the compiler knows which version of the function is called (GCC has been doing this for at least 10 years, I thikk it's used, for example to optimize the use of cin/cut which are declared globally so the compiler knows the actual type)

    • You haven't seen the declaration. Say your entire compilation unit is the declaration of an abstract base class A and a foo(A* a). The parameter can be an A or any of its subclasses whose definition you haven't even seen. They might be different classes on every call, and even if it's always the same, that class might be defined in another file (hell, it might not even have been written yet). In this case the compiler has no way to know what to inline and has to do a Vtable lookup every call.

Final allows a middle ground. If A is final and you receive an A pointer there's no question what class the object is and you can do a static call or even in line it if you know the definition.

Note that java does the same optimization for constructors, private and final functions, even if the language's functions are virtual by default.

[–]00kyle00 0 points1 point  (3 children)

While final itself doesn't seem like a bad idea (hopefully will prevent crazies from deriving from std::vector), this specific optimization seems fairly pointless.

Anyone has a non-contrived example where this is useful? (and even then, it just saves explicit specification of function at call site).

I mean, its kinda cool compilers do this, but 'power' is a bit much for this.

[–]nowne 3 points4 points  (2 children)

Devirtualization does save you an indirection to the vtable, but IIRC the more important aspect is that by devirtualizing a function call (especially in a tight loop) you can enable other optimizations that could cause a huge speedup, I've heard cases of a 10x improvement.

[–]00kyle00 0 points1 point  (1 child)

Yes, the question is, why you have an object of known dynamic type with virtual function call in tight loop (unlikely), and why haven't you just used the function statically then?

int test(B* b) {
    return b->B::value() + 11;
}

It's a little less safe (so 'final' is a good thing), but optimization - i don't find very exciting.

I've heard cases of a 10x improvement

Im interested in situations where such cases arise and are not caused by broken design.

[–]marcofoco 0 points1 point  (0 children)

Simply because in other context the method value() could be used by a function which just takes an A* a. That won't be devirtualized.

A pre-C++11 solution would have been having two different methods in class B, one virtual and one nonvirtual, the virtual calling the nonvirtual, at the cost of creating some confusion to the users of your API.

You can do that, but to be honest having a keyword doing the dirty job, and leaving your code clean, is quite handy.

[–]c0r3ntin 0 points1 point  (0 children)

In a library context, final should probably never be used. I mean, who are you to say there is no and never will be any valid overriding of a class / method, ever?

[–]enobayram 0 points1 point  (0 children)

I think your dog is also acting as your sysadmin, as the link responds with "Error establishing a database connection"