2

I try to better understand the difference between const from C/C++ and intent(in) in Fortran in the context of classes.

C++: If I const the implicit this argument to a member function and I return references to members of this, these references have to be const T*. So the const does not only apply to the scope of the function call, but also extends to all references "created" in that function call.

Fortran: If I intent(in) the this argument of a member function the intent(in) only applies to the scope of the member function. I cannot mutate this inside the member function, but I can return a reference and modify this outside.

If I have the following more or less equivalent code for a Counter class in Fortran and C++ to test this and it seems to be correct.

#include <iostream>


class Counter {
    int val{};

public:
    auto next()
    {
        return val++;
    };

    const auto raw() const
    {
        const auto ptr = &val;
        return ptr;
    };
};

int main()
{
    Counter c{};
    std::cout << c.next() << "\n";
    std::cout << c.next() << "\n";

    auto ptr = c.raw();
    // does not compile, as it is expected
    *ptr = 0;
    std::cout << c.next() << "\n";

    return 0;
}
module counter_mod
    implicit none(type, external)
    private
    public :: Counter_t

    type :: Counter_t
        private
        integer :: val = 0
    contains
        procedure :: next
        procedure :: raw
    end type

contains

    integer function next(this)
        class(Counter_t), intent(inout) :: this
        next = this%val
        this%val = this%val + 1
    end function

    function raw(this) result(res)
        class(Counter_t), target, intent(in) :: this
        integer, pointer :: res
        ! This would be forbidden
        ! this%val = 5
        res => this%val
    end function
end module

program test_raw
    use counter_mod, only: Counter_t
    implicit none(type, external)

    type(Counter_t), target :: c
    integer, pointer :: ptr

    write(*, *) c%next()
    write(*, *) c%next()

    ptr => c%raw()
    ptr = 0
    write(*, *) c%next()
end program
mcocdawc
  • 1,748
  • 1
  • 12
  • 21
  • you can return a non-const reference / pointer from a `const` method, just not to a member because they are const – 463035818_is_not_an_ai Oct 08 '20 at 10:10
  • 2
    what is the question? – 463035818_is_not_an_ai Oct 08 '20 at 10:10
  • My question is, if it is correct what I wrote. – mcocdawc Oct 08 '20 at 10:49
  • `// does not compile as expected` why do you expect `*ptr = 0;` to compile fine? This somehow contradicts what you wrote before. You already know that is is not correct, no? – 463035818_is_not_an_ai Oct 08 '20 at 10:53
  • That was bad English on my part. It should be `// does not compile, as it is expected` – mcocdawc Oct 08 '20 at 10:54
  • oh now I get it. It is "(does not compile) as expected" not "does not (compile as expected)" ;) – 463035818_is_not_an_ai Oct 08 '20 at 10:55
  • I don't understand your question, but maybe it relates to "but I can return a reference and modify `this` outside". `this` is local to the function and there's no "outside" where it exists. You can modify `c` in the main program, but that's a different entity. – francescalus Oct 08 '20 at 10:56
  • 1
    you are basically asking two questions then. Is the C++ code correct and is the fortran code correct. Apart from the one line the C++ code looks ok, I cannot say anything about Fortran, last time I used it, it looked completely different – 463035818_is_not_an_ai Oct 08 '20 at 10:57
  • 1
    @mcocdawc To get more info, I've forwarded your question to the [Fortran discourse site](https://fortran-lang.discourse.group/t/about-a-const-read-only-reference-to-type-component/339) which is somewhat more oriented toward open questions and discussions. I think it will be great if you add comments also there (so that the readers will also understand the intent more clearly <-- pun intended :). – roygvib Oct 10 '20 at 09:16
  • @roygvib thank you for forwarding my question. Yes I would really like to have pointers to `const` in Fortran as well. – mcocdawc Oct 15 '20 at 11:23

2 Answers2

1

In c++ You do not need to return a const pointer you can simply return a const reference. This is the typical getter implementation eg:

const method means that the instance properties will not be modified by the method.

class Counter {
    int val{};

public:
    auto next()
    {
        return val++;
    };

    const int &getVal const
    {
        return val;
    };
};

In the exemple above, the getter could also return a copy of val. (no need for returning a const reference in that case).

Jean-Marc Volle
  • 3,113
  • 1
  • 16
  • 20
  • > In the exemple above, the getter could also return a copy of val. I know, but my member in reality is much larger. – mcocdawc Oct 15 '20 at 11:19
0

After reading the comments and answers regarding the C++ part of my question and consulting the Fortran 2008 standard, I think I can answer my own question:

C++: It is not possible (without hacky tricks like const_cast) to create a T* pointer or T& reference to a const T variable of type T. The pointers/references have to be to const as well. Hence: if this is a const argument to our member function, we are not allowed to return non-const pointers or references.

int& getVal() const {return val;};
int* getVal() const {return &val;};

would both not compile. So by declaring this as const we can give read-only access to our members. (And thanks to @Jean-Marc Volle I will use the first version.)

Fortran: The underlying problem in Fortran is, that there are no pointers to const (const T*), but only const pointers (T* const). So it is not possible to give read-only access with a pointer to components of a type (members of a class in C++). The remaining question is between the difference of const and intent(in) and if it is possible to return a possibly mutating reference to an object (or its components) of intent(in).

In the Fortran 2008 standard 5.3.10 it says:

The INTENT (IN) attribute for a nonpointer dummy argument specifies that it shall neither be defined nor become undefined during the invocation and execution of the procedure. The INTENT (IN) attribute for a pointer dummy argument specifies that during the invocation and execution of the procedure its association shall not be changed except that it may become undefined if the target is deallocated other than through the pointer (16.5.2.5).

If I interpret it correctly, it is not allowed to mutate an intent(in) object, but it is possible to return a (possibly) mutating reference.

mcocdawc
  • 1,748
  • 1
  • 12
  • 21