Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Does SFINAE apply to non-template functions?

1 view
Skip to first unread message

Marian Ciobanu

unread,
Sep 13, 2007, 12:49:29 PM9/13/07
to
Hi,


Until recently I was convinced that SFINAE only applies to template
functions, to remove template functions that have an error in their
signature from a list of possible candidates for a call. The errors
considered are limited to "missing type", "nonsensical type" and
perhaps others, but don't include things like "negative-size arrays".

I had some posts in another thread, where I've constantly been told
that SFINAE also applies to non-template functions that are member
of a template class. That thread is:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/56c346896185e23e/aff6cc49a191445d

(if the line above is broken try http://tinyurl.com/2o2lol or search
in your newsreader for a post on September 6 called "Is this valid
code?")

Well, I wonder if that is true and if anybody has an example that
works across various compilers showing how SFINAE applies to
non-template functions.

To increase the chances of my question being understood: to me the
member function "f" below is "non-template", while "g" is a "template
function".

template <typename T>
struct A
{
int f();
template <typename U> int g(U);
};

Thanks,
Ciobi

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

Daniel Krügler

unread,
Sep 16, 2007, 6:25:23 PM9/16/07
to
On 13 Sep., 18:49, Marian Ciobanu <ci...@inbox.com> wrote:
> Until recently I was convinced that SFINAE only applies to template
> functions, to remove template functions that have an error in their
> signature from a list of possible candidates for a call. The errors
> considered are limited to "missing type", "nonsensical type" and
> perhaps others, but don't include things like "negative-size arrays".
>
> I had some posts in another thread, where I've constantly been told
> that SFINAE also applies to non-template functions that are member
> of a template class.

The standard does not use the notion of SFINAE, but what
"function-SFINAE"[1] takes advantage of is limited to function
templates and one requirement for a working SFINAE-based overload
selection is that at least one member exists in the viable set.
The mainly relevant section is [temp.deduct]/2 and this always
talks about function templates. I'm not aware of other (non-
template-) related parts of the standard which could be interpreted
as SFINAE-capable.

> To increase the chances of my question being understood: to me the
> member function "f" below is "non-template", while "g" is a "template
> function".
>
> template <typename T>
> struct A
> {
> int f();
> template <typename U> int g(U);
>
> };

f is a member function of the class template A, g is a member
function template of the class template A.

Unfortunately the standard itself is somewhat inprecise here.
Both the official standard 14882:2003(E) as well as the most
recent draft N2369 provide the following example in
[temp.mem.func]/1:

"template<class T> class Array {
T* v;
int sz;
public:
explicit Array(int);
T& operator[](int);
T& elem(int i) { return v[i]; }
// ...
};

declares three function templates.[..]"

This is an obvious editorial issue.

Greetings from Bremen,

Daniel Krügler

[1]: SFINAE can also occur during selection of class template
specializations.

Greg Herlihy

unread,
Sep 17, 2007, 1:19:11 PM9/17/07
to
On Sep 13, 9:49 am, Marian Ciobanu <ci...@inbox.com> wrote:
> Until recently I was convinced that SFINAE only applies to template
> functions, to remove template functions that have an error in their
> signature from a list of possible candidates for a call. The errors
> considered are limited to "missing type", "nonsensical type" and
> perhaps others, but don't include things like "negative-size arrays".
>
> I had some posts in another thread, where I've constantly been told
> that SFINAE also applies to non-template functions that are member
> of a template class.
>
> Well, I wonder if that is true and if anybody has an example that
> works across various compilers showing how SFINAE applies to
> non-template functions.

The answer to the question that you asked is "yes", but the answer to
the question you had in mind - is "no". SFINAE ("Substitution failure
is not an error") is really a broad principle of language design. As
such, it omits one important detail: the substitution - of what? - "is
not an error"? Well, the "what", it turns out, can vary. In C++, the
substitution of a function parameter by a function call argument is
one example of SFINAE:

void f( int );
void f( const std::string& );

int main()
{
f( "text" );
}

In this example, the compiler first attempts to substitute the int
parameter in the first f() declaration with the string literal
argument passed to f() in the function call in main(). The attempted
substitution fails (a std::string does not implicitly convert to an
int) but the failure to substitute the parameter with the function
call argument is not an error in C++. (Instead, the first f()
declaration is excluded from the set of functions that could handle
the call). So the answer to the question asked is "yes": function
overloading is one example of SFINAE in C++ that involves ordinary
functions

A more helpful answer, though, would take into account that the
question really refers to a different kind of substitution in C++:
namely, the substitution of a parameterized type in a function
template with a type deduced from an argument in a function call. In
this case, if an attempted type substitution fails (in one of the ways
enumerated in the C++ Standard), then the failure is not a compile-
time error - instead the compiler effectively ignores the failed
substitution.

Since no parameter types need to be deduced in a call to an ordinary
function (because the types of the parameters have already been
specified in the function's declaration), the only situation when type
deduction is performed - is when a function template is invoked.
Therefore, the answer to the question (assuming that SFINAE really
means "type deduction"), is "yes" - there is no type deduction (and
therefore no SFINAE) in the resolution of a call to a non-template
function - so the invoked function itself must be a template. For
example:

template <template <typename> class C, typename T>
class is_defined
{
typedef char no;
typedef char (&yes)[2];
static no fn( ... );
static yes fn( C<T>);
static C<T> const& make();
public:
static const bool value = sizeof( fn( make())) ==
sizeof(yes);
};

The size of is_defined< C<T> >::value depends on which fn() is
selected to match the fn() that appears in the sizeof() expression. So
the compiler has to decide whether the return type of make(), a const
C<T>, is a better match for a C<T> function parameter, or for an
ellipsis parameter. Of course a C<T> parameter will always be a better
match for a const C<T>& argument (no matter which C and T were used to
instantiate is_defined). So the fn() that returns "yes" will always be
selected in the sizeof expression - therefore is_defined::value is
certain to equal 2.

Note that if fn() were actually called, but failed to compile for some
reason, the substitution would still be considered a success. In other
words, an error when calling fn() would not mean that the substitution
(of the C<T> parameter with the const C<T> argument) itself "failed".
On the contrary, such an error would prove the opposite: that the
substitution succeeded. After all, the substitution must have
succeeded, otherwise there would have been no possibility that a
subsequent error could have occurred. (And whether the particular
error happened to be due to an incomplete type, private constructor or
some other reason, would have no bearing on the question of whether a
const C<T>& argument may substitute for a C<T> parameter - it may).

Greg

0 new messages