2

I am puzzled with the following 2 simple snippets:

#include <vector>

struct A{
    int foo(int i) const {return v[i];}

    std::vector<int> v;
};

int f(const A &a, int i) {
    int j;
    j=a.foo(i);
    j=a.foo(i);
    return j;
}

which gives the assembly code:

movsxd  rax, esi
mov     rcx, qword ptr [rdi]
mov     eax, dword ptr [rcx + 4*rax]
ret

and

#include <vector>

struct A{
    int foo(int i) const;

    std::vector<int> v;
};

int f(const A &a, int i) {
    int j;
    j=a.foo(i);
    j=a.foo(i);
    return j;
}

which gives:

push    rbp
push    rbx
push    rax
mov     ebp, esi
mov     rbx, rdi
call    _ZNK1A3fooEi
mov     rdi, rbx
mov     esi, ebp
add     rsp, 8
pop     rbx
pop     rbp
jmp     _ZNK1A3fooEi            # TAILCALL

In the first case, the compilers 'sees' the internal of the function foo and get that he does not have to call it twice. In the second case, it just has its declaration, and it seems that it cannot assume that it can call the function just once, whereas the function is const... Why is that? Is this an aliasing problem between i and the internal of the function foo? If this is an aliasing problem, how can I ensure the compiler not to worry about that?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
janou195
  • 1,175
  • 2
  • 10
  • 25
  • 8
    Unless I'm missing something, both code blocks are the same – NathanOliver Feb 15 '21 at 19:03
  • No, C++ only has notion of compile-time immutability (`constexpr`). `const` modifier on non-static member functions is used only for overload resolution. – user7860670 Feb 15 '21 at 19:04
  • @NathanOliver: Looks the same to me too. OP please edit to fix the examples. – ecm Feb 15 '21 at 19:04
  • @NathanOliver You are right, unless my diff viewer is also missing something. – Lukas-T Feb 15 '21 at 19:05
  • 3
    The fact that the function is `const` doesn't mean that it can't return different values every time you call it. It only means that the function will not change `*this`. – Ted Lyngmo Feb 15 '21 at 19:07
  • 2
    Some compilers have ways to specify that but it's not standard. See for example [gcc __attribute__(const)](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute) – Jester Feb 15 '21 at 19:12
  • Sorry for the mistake I edited the question... – janou195 Feb 15 '21 at 19:16
  • 4
    @janou195 The function the compiler does *not* see could be `int A::foo(int i) const { return rand(); }` for example. – dxiv Feb 15 '21 at 19:18
  • 2
    Where's the implementation of the second `A.foo()`? What's the purpose of the double `j=a.foo(i);`? – Ted Lyngmo Feb 15 '21 at 19:18
  • @TedLyngmo: IDK if it was edited later, but it seems clear the purpose is to see if the compiler will [CSE](https://en.wikipedia.org/wiki/Common_subexpression_elimination) the two calls into one, because the OP thinks/hopes a `const` function implies "pure" and thus safe to optimize away when you don't need the result (but it doesn't). It's a fair question, although it has probably been answered before at least in other forms. – Peter Cordes Feb 16 '21 at 01:38

1 Answers1

8

The fact that the member function is const-qualified does not mean that the function does not have any side effects. Without seeing function definition, the compiler cannot be sure that the function doesn't output something into a file, doesn't send something over the network, etc. Thus, in general, calling the same member function only once may not produce results equivalent to calling it twice (despite the function being const-qualified).

Edit: To answer the original question: currently, there is no standard way to mark a function as having no side effects. Some compilers may support custom attributes, such as [[gnu::pure]] and [[gnu::const]], but this is not standard.

heap underrun
  • 1,846
  • 1
  • 18
  • 22
  • 1
    This is accurate, but doesn’t answer the question, which is how to communicate to the compiler that the function has no side effects. – prl Feb 15 '21 at 19:20
  • 1
    C++ `[[pure]]` was proposed in [N3744 (2013)](https://cplusplus.github.io/EWG/ewg-active.html#75) and [P0078R0 (2015)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0078r0.pdf) but has not made it into the standard (yet?). – dxiv Feb 15 '21 at 20:56
  • A `const` member function just means `const T *this`. It can still mutate a global variable (e.g. a cache of something even if it's pretending to be read-only), or memory pointed to by its args; I/O isn't the only possible side-effect. (Or even cast away the `const` on some pointers...) – Peter Cordes Feb 15 '21 at 22:03