Search code examples
c++c++11constantsreturn-valueauto

Returning by const value and assigning to a non const variable compiles


I don't understand why the following code is allowed to use the non const version of foo. I would like to forbid a const Array variable to provide a non const ArrayView.

#include <iostream>

struct ArrayView
{
    ArrayView() = default;
    ArrayView(const ArrayView&) = delete;
    ArrayView(ArrayView&&) = delete;

    void foo() {std::cout << "non const" << std::endl;}
    void foo() const {std::cout << "const" << std::endl;}
};

struct Array
{
    const ArrayView createView() const
    {
        return {};
    }
};

int main()
{
    const Array arr{};
    auto view = arr.createView();
    view.foo();

    return 0;
}

output:

non const

I have gcc 10.1.0 on windows 10.


Solution

  • The rule of type deduction for auto is same as template argument deduction; view is declared as non-reference and the const part of the return value of createView is ignored. As the result the type of view is ArrayView, not const ArrayView.

    On the other hand, if you call foo on the return value directly, the const version would be selected. e.g.

    arr.createView().foo();
    

    As the workaround you can declare view as const explicitly,

    const auto view = arr.createView();
    

    Or declare view as reference.

    auto& view = arr.createView(); // the type of view is const ArrayView&
    

    PS: I noticed that you marked both the copy/move constructor of ArrayView as delete. Since C++17 in auto view = arr.createView(); they'll be elided completely because of mandatory copy elision. It doesn't matter here whether they're usable or not.