3

I read that, in simplified terms, if the C++ compiler can prove that a variable is of a certain type, it can inline virtual functions. Why can't it do it in this case (tested with MSVC 2017, clang 4.0, gcc 7.2 and icc 17):

#include <iostream>

class Callable {
public:
    virtual int sq(int n) const;
};

class Square : public Callable {
public:
    inline int sq(int n) const override final  {
        return n*n;
    }
};

class Call {
public:    
    const Callable caller;
};

class Methods {
public:
    constexpr static const Call c {Square()};
};

int main() {
    using std::cout;
    cout << Methods().c.caller.sq(5);
}

I noticed that clang's optimization output in godbolt (link below) says that Callable::sq will not be inlined because its definition is unavailable. However, Methods::c is static and const. Call::caller is also const. Also, Square::sq is final. As far as I know, there is no way they'll change at runtime. Or am I missing something?

On the other hand, the compiler is able to inline the function in this version:

#include <iostream>

class Callable {
public:
    virtual int sq(int n) const;
};

class Square : public Callable {
public:
    inline int sq(int n) const override final  {
        return n*n;
    }
};

class Call {
public:    
    Callable const* caller;
};

class Methods {
public:
    Call c {new Square()};
};

int main() {
    using std::cout;    
    cout << Methods().c.caller->sq(5);
}

Why is that so? Link for godbolt for easy checking.

andre_ss6
  • 1,195
  • 1
  • 13
  • 34
  • what is "clang's optimization view"? – xaxxon Aug 23 '17 at 02:28
  • @xaxxon I meant optimization output. I'll fix it. And I'm not sure about clang, the compiler specifically, but it's a feature in godbolt anyway. You can see it in the link I provided, clicking on "Show optimization output". It shows the compiler's steps trying to optimize the code. – andre_ss6 Aug 23 '17 at 02:35
  • How do you know that clang cannot inline it and that it didn't just choose not to? Where do you see it saying: "Callable::sq will not be inlined because its definition is unavailable" I'm not seeing that anywhere – xaxxon Aug 23 '17 at 02:37
  • @xaxxon ...? Pragmatically, those two things are the same. The question would simply change to "why did it choose not to inline here but did so there?". And based on the output I just mentioned, it looks like it really can't. – andre_ss6 Aug 23 '17 at 02:41
  • Unless you find someone who works on the optimizer willing to put significant time digging into your exact code, you're not likely to get a solid answer on this. Optimizers do the best they can and sometimes they fail to do the best thing. That's normal. If it's a performance critical section, benchmark it and if you need to write asm to guarantee certain execution path, then that's what you have to do. If it's not performance critical, then just move on. Regardless, if you don't know what's going on, your question shouldn't make statements that say that you know something that you don't – xaxxon Aug 23 '17 at 02:43
  • And even if you did get an answer, it could completely change as soon as you change the compiler version you're using. – xaxxon Aug 23 '17 at 02:46
  • 1
    Taking a closer look at this, I'm actually surprised it works at all. `caller` is slicing a `Square`, and `Callable::sq` isn't ever defined (and polymorphic function calls require invocation via a pointer/reference type; full object types (that have been sliced) don't get polymorphic function calls). It's possible this is undefined behavior. – Cornstalks Aug 23 '17 at 02:49
  • @xaxxon Did you ever think that maybe there's something I don't know about the C++ spec that forbids inlining virtual functions in this case? Or maybe I simply don't understand it enough and need clarification? This is the reason SO exists. To asks questions. To get answers from those who know more than you do. If I get no answer, fine. There's no obligation to answer me. About "making statements that say you know something you don't": feel free to edit my question and change "can't" to "won't". That way nobody is misled into believing clang can't do things because of my _very powerful words_. – andre_ss6 Aug 23 '17 at 02:53
  • 1
    @andre_ss6 try making `sq` pure virtual, and you'll see that you create an instance of `Callable`. When you call `sq` in the first example, the called method is the `Callable` definition, because you have no instance of `Square`. You effectivly slice your `Square` instance in your variable of type `Callable`. – Guillaume Racicot Aug 23 '17 at 02:54
  • I didn't know if you didn't know things or not, which is why I asked questions. When I learned that you were making it difficult to answer your question by stating guesses as facts, that's when I mentioned that you shouldn't do that. – xaxxon Aug 23 '17 at 02:54
  • 5
    @andre_ss6 It cannot inline the call because there's nothing to inline. You didn't provide a definition for `Callable::sq`. The function you are calling in you first example is `Callable::sq`, not `Square::sq`. – Guillaume Racicot Aug 23 '17 at 02:55
  • I think `caller` should be declared either as a reference or pointer (as in the second code block) if you want to 'assign' derived class instances to a base class (actually by assignment i mean assigning references). – stephematician Aug 23 '17 at 05:55
  • @stephematician Yes, @GuillaumeRacicot was right (btw, thanks :)). This was the problem after all. I've just started with C++ and I had forgotten that polymorphism works differently here. After making it a reference, I'm able to use `const static ` and get the compiler to inline the method. – andre_ss6 Aug 23 '17 at 18:05

1 Answers1

0

Try adding {} at the end of the virtual function declaration.

 virtual int sq(int n) const {}
Abdallah
  • 402
  • 4
  • 14