Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: using declaration and overload paradigm

I was looking at this page about "new" features of C++17. In particular I understand almost all of the following code:

#include <iostream>
#include <variant>

struct Fluid { };
struct LightItem { };
struct HeavyItem { };
struct FragileItem { };

template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;

int main() {
    std::variant<Fluid, LightItem, HeavyItem, FragileItem> package;

    std::visit(overload{
        [](Fluid& )       { std::cout << "fluid\n"; },
        [](LightItem& )   { std::cout << "light item\n"; },
        [](HeavyItem& )   { std::cout << "heavy item\n"; },
        [](FragileItem& ) { std::cout << "fragile\n"; }
    }, package);
}

What I do not understand is the

using Ts::operator()...;

inside de definition of struct overload. To my knowledge such use of using keyword is to ensure operator() to be public. But it seems to me already public, as far as lambdas are involved. So I would say it is redundant. I try to compile and execute with just

template<class... Ts> struct overload : Ts... { };

and everything works fine. Am I wrong? Am I missing something?

like image 297
MaPo Avatar asked Feb 02 '26 09:02

MaPo


1 Answers

using purpose is not only for changing accessibility, but to "resolve" ambiguity and/or unhide base methods.

See https://en.cppreference.com/w/cpp/language/unqualified_lookup#Member_function_definition

otherwise, if the declaration sets in Bi and in C are different, the result is an ambiguous merge: the new lookup set of C has an invalid declaration and a union of the subobjects ealier merged into C and introduced from Bi. This invalid lookup set may not be an error if it is discarded later.

struct Base1
{
    void foo() {}
};

struct Base2
{
    void foo(int) {}
};

struct D : Base1, Base2
{
    // using Base1::foo; // Those would solve the ambiguity
    // using Base2::foo;

    void bar()
    {
        foo(); // ambiguous
    }
}

struct D2 : Base1
{
    // using Base1::foo; // This would unhide Base1::foo

    void foo(char) {} // hides Base1::foo

    void bar()
    {
        foo(); // wrong, find D2::foo(char)
    }
}

using Ts::operator()...; is then just the variadic syntax.

like image 146
Jarod42 Avatar answered Feb 03 '26 23:02

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!