0

Update, thanks to AlexD's answer: This question boils down to the language rules for selecting member function overloads, which is discussed in the following question: Calling a const function rather than its non-const version


When the range expression of a range-based for loop is a call to a member function with const and non-const overloads, it seems that the non-const overload is selected. As a result, the following program does not compile:

#include <iostream>
#include <vector>

class foo {
    public:
        const std::vector<int>& get_numbers() const { return numbers; }
    protected:
        std::vector<int>& get_numbers() { return numbers; }
    private:
        std::vector<int> numbers;
};

int main() {
    foo f;
    for (int x : f.get_numbers()) std::cout << x << std::endl;
}

Diagnostic message from gcc 5.3:

error: ‘std::vector<int>& foo::get_numbers()’ is protected

But a const version of get_numbers() is available and could be used. We can force it to be used by using a const reference to the foo instance, like this:

int main() {
    foo f;
    const foo& g = f;
    for (int x : g.get_numbers()) std::cout << x << std::endl;
}

Is there a better/easier way to tell the compiler that it can and should use the const member function overload, without explicitly making a const reference to the object?

There are some similar questions about making the range-based for loop use const iterators, but I haven't found any questions about making the loop's range expression const for the purposes of selecting function overloads.

Community
  • 1
  • 1
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • I believe this happens because of `f` declared as `foo f;` and not `const foo f;` That's why the non-const overloaded function is selected over const. My question is why do you need the `getNumbers()` both public and private? – DimChtz Aug 03 '16 at 20:49
  • Avoid any private/protected getter/setter (or use a different name) –  Aug 03 '16 at 20:53
  • @DimChtz Yes, that's the problem. The overload selected is the function signature, not the return type. – doug Aug 03 '16 at 20:54
  • @DieterLücking Yes... this is a contrived example strictly for the purposes of illustrating the problem. This question has nothing to do with proper use of getters, but thanks for your input. – TypeIA Aug 03 '16 at 20:55
  • isn't setting a getter as private kind of defeating the purpose of a getter...? – ifma Aug 03 '16 at 20:58
  • I've changed the example to make the non-`const` getter `protected` instead of `private` (this is the situation in my real code that prompted this question, but I changed it here to shorten the example by one line.) – TypeIA Aug 03 '16 at 21:02

2 Answers2

3

But a const version of get_numbers() is available and could be used.

The best function is selected before accessibility is considered. The standard states (emphasis mine):

If a best viable function exists and is unique, overload resolution succeeds and produces it as the result. Otherwise overload resolution fails and the invocation is ill-formed. When overload resolution succeeds, and the best viable function is not accessible (Clause 11) in the context in which it is used, the program is ill-formed.

AlexD
  • 32,156
  • 3
  • 71
  • 65
  • Thanks, this is good information as far as understanding why the non-`const` overload is selected. But it doesn't quite answer the question of whether there's a more convenient way to select the `const` overload without explicitly making a `const` reference to the instance. – TypeIA Aug 03 '16 at 21:07
  • @TypeIA Some others suggest the same as you did: http://stackoverflow.com/a/7287093 – AlexD Aug 03 '16 at 21:08
  • Thanks, that's a great link and I've added it to the top of my question. That linked question really gets at the heart of this issue, which is unrelated to range-based for loops. – TypeIA Aug 03 '16 at 21:16
0

A simple template function as_const can make the cast not so ugly. I believe this is being added or was recently added to the standard library.

template <typename T> T const & as_const (T const & t) { return t; }

void f ()
{
    for (auto && x: as_const (y)) {}
}
nate
  • 1,771
  • 12
  • 17