Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ overload pattern : call resolution with mutable lambda

Tags:

c++

c++17

Considering this well-known C++ pattern :

template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
template <class... Ts> overload(Ts...) -> overload<Ts...>; // clang needs this deduction guide,
                                                           // even in C++20 for some reasons ...

I wonder why declaring one of the parameter as a mutable lambda changes the override resolution.

Live example here on godbolt :

#include <iostream>

template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
template <class... Ts> overload(Ts...) -> overload<Ts...>; // clang needs this deduction guide,
                                                           // even in C++20 for some reasons ...

auto main() -> int
{
    auto functor_1 = overload{
        [](int &&){
            std::cout << "int\n";
        },
        [](auto &&) {   // making this lambda `mutable` makes deduction mismatch ?
            std::cout << "smthg else\n";
        }
    };
    functor_1(42); // prints `int`

    auto functor_2 = overload{
        [](int &&){
            std::cout << "int\n";
        },
        [](auto &&) mutable {
            std::cout << "smthg else\n";
        }
    };
    functor_2(42); // prints `smth else`
}
like image 899
Guss Avatar asked Mar 31 '21 15:03

Guss


1 Answers

With

auto functor = overload{
    [](int &&){
        std::cout << "int\n";
    },
    [](auto &&) {
        std::cout << "smthg else\n";
    }
};
functor(42); // prints `int`

both closures have const qualified operator()'s so int&& is a better match as it is not a template.

With

auto functor = overload{
    [](int &&){
        std::cout << "int\n";
    },
    [](auto &&) mutable {
        std::cout << "smthg else\n";
    }
};
functor(42); // prints `smthg else`

Your auto&& closure is not const qualified anymore, meaning there is no const qualification adjustment that needs to happen in order to call it. This makes that overload an identity exact match, while the int&& overload needs a const qualification adjustment. The identity exact match beats out a const qualification adjustment exact match per [tab:over.ics.scs] so that is why you see the auto&& version called.

like image 183
NathanOliver Avatar answered Oct 16 '22 17:10

NathanOliver