Date: Tue, 11 Oct 2022 00:10:17 +0200
On 10/10/2022 23.11, Federico Kircheis via Std-Discussion wrote:
> On 10/10/2022 23.00, Edward Catmur wrote:
>>
>>
>> On Mon, 10 Oct 2022 at 21:07, Federico Kircheis via Std-Discussion
>> <std-discussion_at_[hidden]
>> <mailto:std-discussion_at_[hidden]>> wrote:
>>
>> If this is the wrong mailing list, sorry, maybe you can redirect
>> me to
>> the correct one.
>>
>>
>> Yes, this is the right place.
>>
>> Consider following program.
>>
>>
>> ----
>> namespace ns{
>> struct foo{
>> friend void bar(const foo&){}
>> };
>> }
>>
>> void bar2(ns::foo& f){
>> bar(f);
>> }
>>
>> auto lambda = [](ns::foo& f_){bar(f_);};
>>
>> struct foo2{
>> ns::foo mf;
>> void bar(){
>> bar(this->mf); // (1)
>> auto& f2 = mf; [&f2]{bar(f2);}(); // (2)
>> [](ns::foo& f_){bar(f_);}(mf); // (3)
>> bar2(mf); // (4)
>> lambda(mf); // 5
>> ns::bar(mf); // (6)
>> ::bar(mf); // (7)
>> }
>> };
>> ----
>>
>> 1) does not compile, tries to call this->bar, no great, but OK
>>
>> 2,3) Still tries to call this->bar, even if this has not been
>> captured
>>
>> 4,5) Works
>>
>> 6,7) do not compile, as expected
>>
>> (tested with GCC, clang and MSVC)
>>
>>
>> I understand why 1,4,5,6,7 behave as they do, but fail to see why
>> 2 and
>> 3 should not compile and call the hidden friend function.
>>
>>
>> Name lookup proceeds through parent scopes before considering ADL.
>> https://eel.is/c++draft/basic.lookup#unqual-2.sentence-2
>> <https://eel.is/c++draft/basic.lookup#unqual-2.sentence-2>
>> The parent scope of a lamba scope is the scope that contains it.
>> http://eel.is/c++draft/basic.scope#scope-2.1
>> <http://eel.is/c++draft/basic.scope#scope-2.1>
>>
>> 4 and 5 are suboptimal workarounds because they need a new
>> function, if
>> the code is templated it needs to go in the header file, and thus
>> leak
>> an implementation detail, if 2 or 3 would work it would be
>> possible to
>> keep the workaround local.
>> Or is there another way to tell the compiler not trying to call
>> this->bar, without resorting to 6,7 as they do not work in this case?
>>
>>
>> Yes, you can bring another namespace scope function `bar` into scope,
>> then ADL will be considered (because the lookup set does not contain
>> member functions).
>>
>> namespace spoiler { void bar(); }
>> // ...
>> auto& f2 = mf; [&f2]{using spoiler::bar; bar(f2);}(); // (2)
>>
>> It's not a fully local solution, but `::spoiler::bar()` does not need
>> to be defined or to know anything about `::ns::foo`.
>>
>> I tried finding the relevant part in the standard why 2 and 3 do not
>> work, but failed to do so, can anyone pinpoint me in the right
>> direction?
>>
>> Best
>>
>> Federico
>> --
>
>
> Thank you Edward for the reference to the relevant part of the standard,
> very appreciated.
>
> I'll write the workaround with "namespace spoiler { void bar(); }" down,
> it works as advertised.
>
> It's not a "local solution", but at least I do not need to define a
> dummy function, which is not always that easy if one considers templated
> code, perfect forwarding, ....
>
>
> Which makes me realize, I can write
>
>
> auto& f2 = mf; [&f2]{void bar(ns::foo&); bar(f2);}();
>
> But only works if the function signature is the correct one.
> For example
>
> auto& f2 = mf; [&f2]{void bar(); bar(f2);}();
>
> does not seem to work, even if I would have expected it (and hoped), as
> "spoiler::bar" also does not have the right function signature.
In fact the lambda is not even necessary
void bar(const ns::foo&); bar(mf); // works
using spoiler::bar; bar(mf); // works
void bar(); bar(mf); // does not work, contrary to spoiler, very unfortunate
But why does this example with operator== work without any workarounds?
> On 10/10/2022 23.00, Edward Catmur wrote:
>>
>>
>> On Mon, 10 Oct 2022 at 21:07, Federico Kircheis via Std-Discussion
>> <std-discussion_at_[hidden]
>> <mailto:std-discussion_at_[hidden]>> wrote:
>>
>> If this is the wrong mailing list, sorry, maybe you can redirect
>> me to
>> the correct one.
>>
>>
>> Yes, this is the right place.
>>
>> Consider following program.
>>
>>
>> ----
>> namespace ns{
>> struct foo{
>> friend void bar(const foo&){}
>> };
>> }
>>
>> void bar2(ns::foo& f){
>> bar(f);
>> }
>>
>> auto lambda = [](ns::foo& f_){bar(f_);};
>>
>> struct foo2{
>> ns::foo mf;
>> void bar(){
>> bar(this->mf); // (1)
>> auto& f2 = mf; [&f2]{bar(f2);}(); // (2)
>> [](ns::foo& f_){bar(f_);}(mf); // (3)
>> bar2(mf); // (4)
>> lambda(mf); // 5
>> ns::bar(mf); // (6)
>> ::bar(mf); // (7)
>> }
>> };
>> ----
>>
>> 1) does not compile, tries to call this->bar, no great, but OK
>>
>> 2,3) Still tries to call this->bar, even if this has not been
>> captured
>>
>> 4,5) Works
>>
>> 6,7) do not compile, as expected
>>
>> (tested with GCC, clang and MSVC)
>>
>>
>> I understand why 1,4,5,6,7 behave as they do, but fail to see why
>> 2 and
>> 3 should not compile and call the hidden friend function.
>>
>>
>> Name lookup proceeds through parent scopes before considering ADL.
>> https://eel.is/c++draft/basic.lookup#unqual-2.sentence-2
>> <https://eel.is/c++draft/basic.lookup#unqual-2.sentence-2>
>> The parent scope of a lamba scope is the scope that contains it.
>> http://eel.is/c++draft/basic.scope#scope-2.1
>> <http://eel.is/c++draft/basic.scope#scope-2.1>
>>
>> 4 and 5 are suboptimal workarounds because they need a new
>> function, if
>> the code is templated it needs to go in the header file, and thus
>> leak
>> an implementation detail, if 2 or 3 would work it would be
>> possible to
>> keep the workaround local.
>> Or is there another way to tell the compiler not trying to call
>> this->bar, without resorting to 6,7 as they do not work in this case?
>>
>>
>> Yes, you can bring another namespace scope function `bar` into scope,
>> then ADL will be considered (because the lookup set does not contain
>> member functions).
>>
>> namespace spoiler { void bar(); }
>> // ...
>> auto& f2 = mf; [&f2]{using spoiler::bar; bar(f2);}(); // (2)
>>
>> It's not a fully local solution, but `::spoiler::bar()` does not need
>> to be defined or to know anything about `::ns::foo`.
>>
>> I tried finding the relevant part in the standard why 2 and 3 do not
>> work, but failed to do so, can anyone pinpoint me in the right
>> direction?
>>
>> Best
>>
>> Federico
>> --
>
>
> Thank you Edward for the reference to the relevant part of the standard,
> very appreciated.
>
> I'll write the workaround with "namespace spoiler { void bar(); }" down,
> it works as advertised.
>
> It's not a "local solution", but at least I do not need to define a
> dummy function, which is not always that easy if one considers templated
> code, perfect forwarding, ....
>
>
> Which makes me realize, I can write
>
>
> auto& f2 = mf; [&f2]{void bar(ns::foo&); bar(f2);}();
>
> But only works if the function signature is the correct one.
> For example
>
> auto& f2 = mf; [&f2]{void bar(); bar(f2);}();
>
> does not seem to work, even if I would have expected it (and hoped), as
> "spoiler::bar" also does not have the right function signature.
In fact the lambda is not even necessary
void bar(const ns::foo&); bar(mf); // works
using spoiler::bar; bar(mf); // works
void bar(); bar(mf); // does not work, contrary to spoiler, very unfortunate
But why does this example with operator== work without any workarounds?
---- namespace ns{ struct foo{ friend bool operator==(const foo&, int){return true;} }; } struct foo2{ ns::foo mf; bool operator==(int i) const { return mf == i; } }; ---- I do not think there is some special rule for operator==, and yet no compiler complains...
Received on 2022-10-10 22:10:22