Topic: Unexpected overload resolution under SFINAE conditions


Author: "=?iso-8859-1?q?Daniel_Kr=FCgler?=" <daniel.kruegler@googlemail.com>
Date: Wed, 28 Mar 2007 11:37:33 CST
Raw View
Daniel Kr   gler schrieb:

> Just recently I stumbled across an astonishing effect that
> occurred during overload selection of free function templates
> under SFINAE conditions.

I would like to provide the following information: I posted
a bug report to Comeau Online and today I got feedback
supporting by view that the described behaviour in my OP
was a compiler bug.
Yes, I say "it was", because they hastened to add that
this has been fixed and can be verified in the new currently
available online version 4.3.9, so try it out on you own:

http://www.comeaucomputing.com/tryitout/

My personal congratulations for that cool, public available
compiler to the Comeau folks!

Greetings from Bremen,

Daniel Kr   gler


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Daniel_Kr=FCgler?=" <daniel.kruegler@googlemail.com>
Date: Sun, 25 Mar 2007 17:43:22 CST
Raw View
Just recently I stumbled across an astonishing effect that
occurred during overload selection of free function templates
under SFINAE conditions.
Let me introduce my demonstration with a common include
header, lets say "Common.h":

// Common.h ---------------------------------------------------------
#include <iostream>
#include <ostream>

template <bool Enable, typename T = void>
struct enable_if { typedef T type; };

template <typename T>
struct enable_if<false, T> {};

template <typename T1, typename T2>
struct is_same { static const bool value = false; };

template <typename T>
struct is_same<T, T> { static const bool value = true; };

template <typename T>
struct Something{};

struct Other{};
// Common.h ---------------------------------------------------------

The first experiment was a simple, non-SFINAE overload
deduction program #1:

// #1 ---------------------------------------------------------
#include "Common.h"

template <class T>
void foo(Something<T>&){ std::cout << "1" << std::endl; }

template <class T>
void foo(T&) { std::cout << "2" << std::endl; }

int main() {
  Something<int> x;
  foo(x);
}
// #1 ---------------------------------------------------------

I think that every-one expects the same here and all
compiler's I tested (Comeau Online, VS2005-SP1,
mingw (gcc 3.4)) agreed that a single best viable
function can be found and that this one is printing "1".

Now this program was slightly modified to "split" the
general handler ("2") into two handlers using SFINAE:

// #2 ---------------------------------------------------------
#include "Common.h"

template <class T>
void foo(Something<T>&){ std::cout << "1" << std::endl; }

template <class T>
typename enable_if<is_same<T, Other>::value>::type
foo(T&) { std::cout << "2a" << std::endl; }

template <class T>
typename enable_if<!is_same<T, Other>::value>::type
foo(T&) { std::cout << "2b" << std::endl;}

int main() {
  Something<int> x;
  foo(x);
}
// #2 ---------------------------------------------------------

Even this program compiled and gave one result ("1" again)
- except for Comeau (I tested all available online versions as
well as two versions used in our company - from here I cannot
say which versions) which choked on an *ambiguity*:

"error: more than one instance of overloaded function "foo"
          matches the argument list, the choices that match are:
            function template "void foo(Something<T> &)"
            function template "enable_if<<expression>, void>::type
foo(T &)"
            The argument types that you used are: (Something<int>)
    foo(x);
    ^

My question is: Can Comeau be right here? I studied large parts
of 13.3/2-4, 13.3.1.1.1, 13.3.2, 13.3.3.1, 14.8.3, 14.8.2 including
a small promenade into 3.4(2), but could not find any evidence
for this outcome, but maybe my reading/interpretation is wrong.

I would happily appreciate any comments as well as some
references in the standard if a constradictory position is taken!

Thanks and Greetings from Bremen,

Daniel Kr   gler


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Sun, 25 Mar 2007 23:45:58 CST
Raw View
On Mar 25, 4:43 pm, "Daniel Kr   gler" <daniel.krueg...@googlemail.com>
wrote:
>
> // #2 ---------------------------------------------------------
> #include "Common.h"
>
> template <class T>
> void foo(Something<T>&){ std::cout << "1" << std::endl; }
>
> template <class T>
> typename enable_if<is_same<T, Other>::value>::type
> foo(T&) { std::cout << "2a" << std::endl; }
>
> template <class T>
> typename enable_if<!is_same<T, Other>::value>::type
> foo(T&) { std::cout << "2b" << std::endl;}
>
> Even this program compiled and gave one result ("1" again)
> - except for Comeau (I tested all available online versions as
> well as two versions used in our company - from here I cannot
> say which versions) which choked on an *ambiguity*:
>
> "error: more than one instance of overloaded function "foo"
>           matches the argument list, the choices that match are:
>             function template "void foo(Something<T> &)"
>             function template "enable_if<<expression>, void>::type
> foo(T &)"
>             The argument types that you used are: (Something<int>)
>     foo(x);
>     ^
>
> My question is: Can Comeau be right here? I studied large parts
> of 13.3/2-4, 13.3.1.1.1, 13.3.2, 13.3.3.1, 14.8.3, 14.8.2 including
> a small promenade into 3.4(2), but could not find any evidence
> for this outcome, but maybe my reading/interpretation is wrong.

   13.1/2 makes it clear that a function declaration may not be
overloaded by another function declaration that differs only in its
return type - yet this program overloads foo() with another a nearly-
identical foo() - that differs only in its return type. Therefore
Comeau is correct to report an error.

Greg


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Daniel_Kr=FCgler?=" <daniel.kruegler@googlemail.com>
Date: Mon, 26 Mar 2007 12:47:13 CST
Raw View
On Mar 26, 7:45 am, "Greg Herlihy" <gre...@pacbell.net> wrote:
> On Mar 25, 4:43 pm, "Daniel Kr   gler" <daniel.krueg...@googlemail.com>
> > My question is: Can Comeau be right here? I studied large parts
> > of 13.3/2-4, 13.3.1.1.1, 13.3.2, 13.3.3.1, 14.8.3, 14.8.2 including
> > a small promenade into 3.4(2), but could not find any evidence
> > for this outcome, but maybe my reading/interpretation is wrong.
>
>    13.1/2 makes it clear that a function declaration may not be
> overloaded by another function declaration that differs only in its
> return type - yet this program overloads foo() with another a nearly-
> identical foo() - that differs only in its return type. Therefore
> Comeau is correct to report an error.

Note that due to SFINAE there will be only *one* of the
seemingly two overloads in the final overload set, because
depending on T the other one will be "masked". Furtheron
14.5.5.1/1 makes it clear that:

"It is possible to overload function templates so that two
different function template specializations have the same
type."

Going on with para 2-4:

"Such specializations are distinct functions and do not
violate the one definition rule (3.2).

The signature of a function template specialization
consists of the signature of the function template and
of the actual template arguments (whether explicitly
specified or deduced).

The signature of a function template consists of its
function signature, its return type and its template
parameter list."

That your point is not relevant here is also clearly
seen from my quoted error message:
Comeau does *not* choke about the two seemingly
equal overloads. A modified program, which *only*
uses the two SFINAE overloads is accepted even
by Comeau:

// #3 ---------------------------------------------------------
#include "Common.h"

template <class T>
typename enable_if<is_same<T, Other>::value>::type
foo(T&) { std::cout << "2a" << std::endl; }

template <class T>
typename enable_if<!is_same<T, Other>::value>::type
foo(T&) { std::cout << "2b" << std::endl;}

int main() {
  Something<int> x;
  foo(x); // Fine, chooses "2b"
}
// #3 ---------------------------------------------------------

Btw.: If your assumption would be true, Concept-based
overloading would not work anyway: It uses practically
the SFINAE ansatz and uses overload sets with the "same"
argument types (but not same signatures). Depending on
the actual argument only one remains as viable best
function overload and will be choosen.

I'm *not* looking for a workaround, I'm just trying to see
a loophole, which Comeau uses, that I do not see yet.

Btw, a possible *workaround* is to create a completely
SFINAEed overload set:

// #4 ---------------------------------------------------------
#include "Common.h"

template <typename T>
struct is_SomeThing { static const bool value = false; };

template <typename T>
struct is_SomeThing<Something<T> > { static const bool value =
true; };

template <class T>
typename enable_if<is_SomeThing<T>::value>::type
foo(T&){ std::cout << "1" << std::endl; }

template <class T>
typename enable_if<is_same<T, Other>::value>::type
foo(T&) { std::cout << "2a" << std::endl; }

template <class T>
typename enable_if<
  !is_same<T, Other>::value &&
  !is_SomeThing<T>::value>::type
foo(T&) { std::cout << "2b" << std::endl;}

int main() {
  Something<int> x;
  foo(x); // Fine, chooses "1"
}
// #4 ---------------------------------------------------------

Greetings from Bremen,

Daniel Kr   gler


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]