From: Kimon Hoffmann on
Hi all,

I have a problem overloading a function located in a namespace for a
custom type. I have constructed the following example (emulating a
preprocessed source file) exhibiting the problem. Although I tried to be
concise with it, it's still quite long, so please bear with me:

----------------------------------------------------------------------

// Code included from DefaultImplementation.hpp

namespace default_impl {

struct DefaultType { /* ... */ };

inline void func(DefaultType const&) {
// .. do something ...
}

}

// Code included from Algorithm.hpp

namespace algorithm {

template<typename T>
void cool_algorithm_1(T const& value) {
using default_impl::func;
// ...
func(value);
// ...
}

template<typename T>
void cool_algorithm_2(T const& value) {
// ...
default_impl::func(value);
// ...
}

}

// Code included from NotWorking.hpp

struct NotWorking { /* ... */ };

namespace default_impl {

inline void func(NotWorking const&) {
// .. do something else ...
}

}

// Code included from HalfWorking.hpp

struct HalfWorking { /* ... */ };

inline void func(HalfWorking const&) {
// .. do something else ...
}

// Code from Main.cpp

int main(int, char**) {

NotWorking notWorking;
// This does not work as the func() overload is not found
algorithm::cool_algorithm_1(notWorking);
// For the same reason this does not work either
algorithm::cool_algorithm_2(notWorking);

HalfWorking halfWorking;
// This *does* work thanks to ADL
algorithm::cool_algorithm_1(halfWorking);
// This does not work because of full qualification
algorithm::cool_algorithm_2(halfWorking);

return 0;
}

----------------------------------------------------------------------

It is obvious why the cool_algorithm_X() functions choke when calling
func() with a constant NotWorking reference, and it is also clear to me
why the correct overload of func() is found from within
cool_algorithm_1() in the HalfWorking case.

What I'd like to know is whether there is some way to make HalfWorking
work with fully qualified calls to func() like the one in
cool_algorithm_2(), besides the obvious solution to move HalfWorking
together with the function overload into the default_impl namespace.

I really appreciate any help on this.

Best regards,
Kimon

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alberto Ganesh Barbati on
Kimon Hoffmann ha scritto:
>
> It is obvious why the cool_algorithm_X() functions choke when calling
> func() with a constant NotWorking reference

Well, what you say it's correct, but it is not *that* obvious. Function
default_impl::func(NotWorking):

1) is not found in binding phase 1 because it is not visible at the
point of definition of the template. (Had it been visible at that point,
the program would compile nicely regardless of the fact that NotWorking
is in the "wrong" namespace.)

2) is not found in binding phase 2 because in neither cases ADL is
triggered (in the first case because default_impl is not an associated
namespace of NotWorking and in the second case because of the full
qualification)

> and it is also clear to me
> why the correct overload of func() is found from within
> cool_algorithm_1() in the HalfWorking case.

Correct, that's because in that case ADL kicks in and the global
namespace is an associated namespace of HalfWorking, so the function is
found in binding phase 2.

> What I'd like to know is whether there is some way to make HalfWorking
> work with fully qualified calls to func() like the one in
> cool_algorithm_2(), besides the obvious solution to move HalfWorking
> together with the function overload into the default_impl namespace.

Given that the target function is not defined in namespace default_impl,
I don't see how you might refer to it with a name fully qualified with
default_impl::.

HTH,

Ganesh

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alberto Ganesh Barbati on
Alberto Ganesh Barbati ha scritto:
> Kimon Hoffmann ha scritto:
>
>> What I'd like to know is whether there is some way to make HalfWorking
>> work with fully qualified calls to func() like the one in
>> cool_algorithm_2(), besides the obvious solution to move HalfWorking
>> together with the function overload into the default_impl namespace.
>
> Given that the target function is not defined in namespace default_impl,
> I don't see how you might refer to it with a name fully qualified with
> default_impl::.
>

Well, of course you could put a using declaration

using ::func;

in namespace default_impl, but you still need to have the definition of
::foo() visible at the definition point of the algorithm. This would
greatly restrict the genericity of the algorithm.

HTH,

Ganesh

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Kimon Hoffmann on
Hi all,

First of all thanks to Alberto Ganesh Barbati for being so kind to
explain the binding in various phases.
After leaving the code alone for one day, a solution occurred to me,
that is not really pretty, but at least it is working as expected.
Here is the revised code:

----------------------------------------------------------------------

// Code included from DefaultImplementation.hpp

namespace default_impl {

struct DefaultType { /* ... */ };

template<typename T>
void func(T const&);

template<>
inline void func(DefaultType const&) {
// .. do something ...
}

}

// Code included from Algorithm.hpp

namespace algorithm {

template<typename T>
void cool_algorithm_1(T const& value) {
using default_impl::func;
// ...
func(value);
// ...
}

template<typename T>
void cool_algorithm_2(T const& value) {
// ...
default_impl::func(value);
// ...
}

}

// Code included from Working.hpp

struct Working { /* ... */ };

namespace default_impl {

template<>
inline void func(Working const&) {
// .. do something else ...
}

}

// Code from Main.cpp
int main(int, char**) {

Working working;
algorithm::cool_algorithm_1(working);
algorithm::cool_algorithm_2(working);

return 0;
}

----------------------------------------------------------------------

My understanding of why this works as expected is that both calls to
func() bind to the primary template forward declaration of func(),
because "Specializations don't overload" (see [1]).
At link time the individual (implicitly specialized) calls to func()
resolve to the explicitly specialized versions of the function and the
code links flawlessly.

As I said in the introduction, this solution is not very pretty. The
first reason is the somewhat unintuitive semantics of function template
specializations (again "Specializations don't overload"), as explained
by Herb Sutter in his article "Why Not Specialize Function Templates?"
[1], and the second reason is that missing specializations are only
detected at link time and thus don't produce compiler errors as one
would expect, but rather linker errors that might be hard to track down.

If I have some time to spare I'll probably restructure the whole thing
to make use of the nice workaround mentioned in the abovementioned article.

Best regards,
Kimon

[1] http://www.gotw.ca/publications/mill17.htm

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Kimon Hoffmann on

Kimon Hoffmann wrote:
>
> What I'd like to know is whether there is some way to make HalfWorking
> work with fully qualified calls to func() like the one in
> cool_algorithm_2(), besides the obvious solution to move HalfWorking
> together with the function overload into the default_impl namespace.
>

for the record, the "obvious solution" does not work either, because ADL
only applies to unqualified function calls and therefor the fully
qualified call to func() from within cool_algorithm_2() will always bind
to the wrong overload.

Best regards,
Kimon


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]