By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,017 Members | 1,093 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,017 IT Pros & Developers. It's quick & easy.

SFINAE revisited

P: n/a
Just ran into an interesting question concerning SFINAE. Given
the following code:

#include <iostream>
#include <typeinfo>

template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;

class B
{
public:
virtual ~B() {}
} ;

int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 15 '08 #1
Share this Question
Share on Google+
35 Replies


P: n/a
On Jul 15, 12:22*pm, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE. *Given
the following code:

* * #include <iostream>
* * #include <typeinfo>

* * template< typename T >
* * class P
* * {
* * public:
* * * * P( T* ) { std::cout << "In P: normal ctor" << std::endl ;}
* * * * template< typename U >
* * * * P( U* ) { std::cout << "In P: template ctor (U = "
* * * * * * * * * * << typeid( U ).name() << ')' << std::endl ; }
* * } ;

* * class B
* * {
* * public:
* * * * virtual * * * * * * ~B() {}
* * } ;

* * int
* * main()
* * {
* * * * class D : public B {} ;
* * * * P< B p( new D ) ;
* * * * return 0 ;
* * }

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. *(My own feeling is that it shouldn't compile. *But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)

Is this any different than having the same set-up with functions
instead of constructors?

template<typename T>
class Foo
{
public:
void goo(T* a) { std::cout << "In Foo: normal fn" << std::endl ;}
template<typename U>
void goo(U* a) { std::cout <<"Int Foo: templated fn"<<std::endl;
};

class B { public: virtual ~B(){} };
class D : public B{};

int main()
{
Foo<Bfoo;
foo.goo(new D);
}

I would think this last line reliably (and rightfully) always chooses
the templated function, but if replaced by "new B" would pick the
"normal fn".

I don't see why Constructors should behave differently (I'd bd curious
if your "three compilers" behave differently in my example.

Joe Cook
Jul 15 '08 #2

P: n/a
James Kanze wrote:
Just ran into an interesting question concerning SFINAE. Given
the following code:

#include <iostream>
#include <typeinfo>

template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;

class B
{
public:
virtual ~B() {}
} ;

int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
First a question: was it intentional that D is a local class? That's
the only reason I see for it not to compile (modulo N2580 :-)).

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 15 '08 #3

P: n/a
James Kanze wrote:
Just ran into an interesting question concerning SFINAE. Given
the following code:

#include <iostream>
#include <typeinfo>

template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;

class B
{
public:
virtual ~B() {}
} ;

int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
SFINAE would kick in if the type deduction would fail due to the
resulting type (or some other element derived from that) is not a
"natural" C++ element, but not an explicitly stated cause of
ill-formedness. OTOH, the Standard is a bit vague WRT using local types
with templates. If you subscribe to the strict spirit of the Standard,
then *any* use of local type as a type argument of a template is not
allowed (according to [temp.arg.type]/2), so the constructor P(U*) that
is instantiated when 'U == D', is an illegal (but not unnatural)
construct. In that case SFINAE does not apply. If you subscribe to the
relaxed Standard, then you shall only avoid using local types when
specifying the template type argument explicitly, but not when you allow
it to be deduced from a function call. In that case SFINAE doesn't
apply either (since the deduction cannot "fail"), and the templated
c-tor is simply used with 'U == D'. Comeau behaves according to the
former strategy, VC++ - according to the latter.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 15 '08 #4

P: n/a
On Jul 15, 7:44 pm, joseph cook <joec...@gmail.comwrote:
On Jul 15, 12:22 pm, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE.
Given the following code:
#include <iostream>
#include <typeinfo>
template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;
class B
{
public:
virtual ~B() {}
} ;
int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
Is this any different than having the same set-up with functions
instead of constructors?
Probably not. I just happened to run into it with constructors.
template<typename T>
class Foo
{
public:
void goo(T* a) { std::cout << "In Foo: normal fn" << std::endl ;}
template<typename U>
void goo(U* a) { std::cout <<"Int Foo: templated fn"<<std::endl;
};
class B { public: virtual ~B(){} };
class D : public B{};
int main()
{
Foo<Bfoo;
foo.goo(new D);
}
I would think this last line reliably (and rightfully) always
chooses the templated function, but if replaced by "new B"
would pick the "normal fn".
Certainly, but you've changed one very important part; in my
code, the derived class is local, which means that it's illegal
to instantiate a template over it.
I don't see why Constructors should behave differently (I'd bd
curious if your "three compilers" behave differently in my
example.
I doubt they do, but your example has nothing to do with my
question.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 15 '08 #5

P: n/a
On Jul 15, 7:54 pm, Gennaro Prota <inva...@yahoo.comwrote:
James Kanze wrote:
Just ran into an interesting question concerning SFINAE. Given
the following code:
#include <iostream>
#include <typeinfo>
template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;
class B
{
public:
virtual ~B() {}
} ;
int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
First a question: was it intentional that D is a local class?
Obviously. Otherwise, there's no reason for it not to compile,
and systematically chose the template constructor.
That's the only reason I see for it not to compile (modulo
N2580 :-)).
I'll have a glance at the paper you cite, but the heart of the
question, of course, is what happens when template argument
deduction comes up with a local class? Is it substitution
failure (in which case, the function is simply dropped, and the
compiler chooses the non-template), or is the substitution
successful, overload resolution then chooses the template
(because it is a better match) and you get an error, because
it's illegal to instantiate the template with a local class.
(The compiler which calls the template function is obviously
wrong.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 15 '08 #6

P: n/a
"Gennaro Prota"
First a question: was it intentional that D is a local class? That's
the only reason I see for it not to compile (modulo N2580 :-)).

I think it is intentional. The template function is a perfect match
only if D is not local to main and so has linkage. The non-template
function is the only one considered otherwise, and is a match with an
implicit conversion.

Fraser.
Jul 15 '08 #7

P: n/a
On 15 juil, 18:22, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE. *Given
the following code:

* * #include <iostream>
* * #include <typeinfo>

* * template< typename T >
* * class P
* * {
* * public:
* * * * P( T* ) { std::cout << "In P: normal ctor" << std::endl ;}
* * * * template< typename U >
* * * * P( U* ) { std::cout << "In P: template ctor (U = "
* * * * * * * * * * << typeid( U ).name() << ')' << std::endl ; }
* * } ;

* * class B
* * {
* * public:
* * * * virtual * * * * * * ~B() {}
* * } ;

* * int
* * main()
* * {
* * * * class D : public B {} ;
* * * * P< B p( new D ) ;
* * * * return 0 ;
* * }

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. *(My own feeling is that it shouldn't compile. *But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)

In your example, a local class is used as a (deduced) template
argument. Local classes can't be used as a template argument. SFINAE
comes when type deduction fails. The C++ standard lists exhaustively
the cases where type deduction fails. The use of a local class as a
template argument is not listed as one of those cases.

Alexandre Courpron.
Jul 15 '08 #8

P: n/a
On Jul 15, 7:56 pm, Victor Bazarov <v.Abaza...@comAcast.netwrote:
James Kanze wrote:
Just ran into an interesting question concerning SFINAE. Given
the following code:
#include <iostream>
#include <typeinfo>
template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;
class B
{
public:
virtual ~B() {}
} ;
int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
SFINAE would kick in if the type deduction would fail due to the
resulting type (or some other element derived from that) is not a
"natural" C++ element, but not an explicitly stated cause of
ill-formedness. OTOH, the Standard is a bit vague WRT using local types
with templates. If you subscribe to the strict spirit of the Standard,
then *any* use of local type as a type argument of a template is not
allowed (according to [temp.arg.type]/2), so the constructor P(U*) that
is instantiated when 'U == D', is an illegal (but not unnatural)
construct. In that case SFINAE does not apply. If you subscribe to the
relaxed Standard, then you shall only avoid using local types when
specifying the template type argument explicitly, but not when you allow
it to be deduced from a function call. In that case SFINAE doesn't
apply either (since the deduction cannot "fail"), and the templated
c-tor is simply used with 'U == D'. Comeau behaves according to the
former strategy, VC++ - according to the latter.
I think that the standard is clear (unless there have been
clarifications I've missed): you can't instantiate a template
over a local class. But you've pretty much summed up the crux
of the problem (at least as I see it): is this a substitution
failure, or does the substitution work, overload resolution pick
up the local class, and then you get an error, because
instantiation over a local class is illegal. That's the opinion
of Sun CC, and a fairly quick reading of the original standard
seems to agree with them. But it's all vague enough, and my
knowledge of this area of the standard is weak enough, that I'm
really not sure.

(FWIW: the problem showed up with a bit of code which was
developed with g++, under Linux. And which passed all of my
tests. Until I ported it to Sun CC under Solaris:-). After
looking at it, my first reaction was that Sun CC was right, and
how did g++ miss this. And then SFINAE occured to me; if there
was a substitution failure, g++ was right. I gave VC++ a try,
since I had access to it, to get a deciding vote, but it took
yet another path---the one you describe. And on reading the
standard, I was even less sure.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 15 '08 #9

P: n/a
co******@gmail.com wrote:
On 15 juil, 18:22, James Kanze <james.ka...@gmail.comwrote:
>Just ran into an interesting question concerning SFINAE. Given
the following code:

#include <iostream>
#include <typeinfo>

template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;

class B
{
public:
virtual ~B() {}
} ;

int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}

Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)


In your example, a local class is used as a (deduced) template
argument. Local classes can't be used as a template argument. SFINAE
comes when type deduction fails. The C++ standard lists exhaustively
the cases where type deduction fails.
Really? I thought that the Standard actually listed exhaustively the
cases where type deduction *succeeded*. Everything else fails. Where
is the exhaustive list of failures? Just point me to the paragraph, no
need to quote it.
The use of a local class as a
template argument is not listed as one of those cases.
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 15 '08 #10

P: n/a
On 15 juil, 21:10, Victor Bazarov <v.Abaza...@comAcast.netwrote:
courp...@gmail.com wrote:
On 15 juil, 18:22, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE. *Given
the following code:
* * #include <iostream>
* * #include <typeinfo>
* * template< typename T >
* * class P
* * {
* * public:
* * * * P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
* * * * template< typename U >
* * * * P( U* ) { std::cout << "In P: template ctor (U = "
* * * * * * * * * * << typeid( U ).name() << ')' << std::endl ; }
* * } ;
* * class B
* * {
* * public:
* * * * virtual * * * * * * ~B() {}
* * } ;
* * int
* * main()
* * {
* * * * class D : public B {} ;
* * * * P< B p( new D ) ;
* * * * return 0 ;
* * }
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. *(My own feeling is that it shouldn't compile. *But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
In your example, a local class is used as a (deduced) template
argument. Local classes can't be used as a template argument. SFINAE
comes when type deduction fails. The C++ standard lists exhaustively
the cases where type deduction fails.

Really? *I thought that the Standard actually listed exhaustively the
cases where type deduction *succeeded*. *Everything else fails. *Where
is the exhaustive list of failures? *Just point me to the paragraph, no
need to quote it.
Explicit template argument :
14.8.2/2

Deduced template argument :
14.8.2.4 (the whole section, specifically paragraphs 11 to 18)
Alexandre Courpron.
Jul 15 '08 #11

P: n/a
"James Kanze"
I think that the standard is clear (unless there have been
clarifications I've missed): you can't instantiate a template
over a local class.

I feel that the compiler should not attempt to instantiate with a local
class. After overload resolution failure a diagnostic might refer to
the local class. The Borland compiler gets everything right as I see
it. The Comeau compiler reports an error too soon for type deduction
failure (in broad terms) and strangely twice.

Fraser.
Jul 15 '08 #12

P: n/a
James Kanze wrote:
>First a question: was it intentional that D is a local class?

Obviously. Otherwise, there's no reason for it not to compile,
and systematically chose the template constructor.
>That's the only reason I see for it not to compile (modulo
N2580 :-)).

I'll have a glance at the paper you cite,
Hmm :-) Then don't beat me!
but the heart of the
question, of course, is what happens when template argument
deduction comes up with a local class? Is it substitution
failure (in which case, the function is simply dropped, and the
compiler chooses the non-template), or is the substitution
successful, overload resolution then chooses the template
(because it is a better match) and you get an error, because
it's illegal to instantiate the template with a local class.
(The compiler which calls the template function is obviously
wrong.)
OK. I see now. I just stopped when I saw the local class and thought
to ask before digging further.

Right now I can't check the standard with the care it deserves, but I
believe it boils down to this: substitution (deduction) *succeeds*,
because "usage of local class for template type argument" isn't listed
as a case of substitution failure; so, a point of instantiation is
created for both constructors, and that leads to an error for each
constructor. This is before there's any chance to add the
instantiations to the overload set, and thus there's really no
overload resolution. But all this should be backed up with a few
paragraph numbers from the standard :-) I'll try doing that tomorrow.

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 15 '08 #13

P: n/a
co******@gmail.com wrote:
On 15 juil, 21:10, Victor Bazarov <v.Abaza...@comAcast.netwrote:
>courp...@gmail.com wrote:
>>On 15 juil, 18:22, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE. Given
the following code:
#include <iostream>
#include <typeinfo>
template< typename T >
class P
{
public:
P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
template< typename U >
P( U* ) { std::cout << "In P: template ctor (U = "
<< typeid( U ).name() << ')' << std::endl ; }
} ;
class B
{
public:
virtual ~B() {}
} ;
int
main()
{
class D : public B {} ;
P< B p( new D ) ;
return 0 ;
}
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. (My own feeling is that it shouldn't compile. But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
In your example, a local class is used as a (deduced) template
argument. Local classes can't be used as a template argument. SFINAE
comes when type deduction fails. The C++ standard lists exhaustively
the cases where type deduction fails.
Really? I thought that the Standard actually listed exhaustively the
cases where type deduction *succeeded*. Everything else fails. Where
is the exhaustive list of failures? Just point me to the paragraph, no
need to quote it.

Explicit template argument :
14.8.2/2

Deduced template argument :
14.8.2.4 (the whole section, specifically paragraphs 11 to 18)
OK, perhaps my understanding of English is failing me, and that's why
when I see
"If a substitution in a template parameter or in the function type
of the function template results in an invalid type, type deduction
fails."
I just conclude that this is all-inclusive rule, deduction resulting in
an invalid type fails. The last sentence of the third bullet item in
the paragraph 2 says "Type deduction *may* fail for the following
reasons:" (with a list of the causes that can make deduction fail, the
emphasis mine). To me it sounds too much like an extension of the note
it follows, as if it says, "Oh, BTW, here is a list of what *may* happen
among other things". BTW, the use of "may" is very tricky AFA standards
are concerned. There is no certainty in it when it comes to (in this
particular case) positive behavior. It opens the door to the deduction
that is allowed to succeed in any of those enumerated situations. For
example, the compiler is *allowed* to deduce the argument even if it
results in a pointer to a reference, if such pointer is not used...
That is probably why I never considered those lists to be exhaustive
enumerations of what is to be treated as the reason for a failure.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 15 '08 #14

P: n/a
"Fraser Ross" <a@zzzzzzzzzzz.comwrote in news:487cfcd5$0$5435$c3e8da3
@news.astraweb.com:
"James Kanze"
I think that the standard is clear (unless there have been
clarifications I've missed): you can't instantiate a template
over a local class.

I feel that the compiler should not attempt to instantiate with a local
class. After overload resolution failure a diagnostic might refer to
the local class. The Borland compiler gets everything right as I see
it. The Comeau compiler reports an error too soon for type deduction
failure (in broad terms) and strangely twice.

Fraser.

I am far from an expert, but it seems to me that it was explicitly
instantiated with a class B (which is not local, but passed in a D * as an
argument with a conversion to B *). I could really seeing going either
way.

joe
Jul 15 '08 #15

P: n/a
Gennaro Prota wrote:
Right now I can't check the standard with the care it deserves, but I
believe it boils down to this: substitution (deduction) *succeeds*,
because "usage of local class for template type argument" isn't listed
as a case of substitution failure; so, a point of instantiation is
created for both constructors, and that leads to an error for each
constructor. This is before there's any chance to add the instantiations
to the overload set, and thus there's really no overload resolution. But
all this should be backed up with a few paragraph numbers from the
standard :-) I'll try doing that tomorrow.
I couldn't resist checking at least the core issues :-) This should be
useful:

<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#488>

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 15 '08 #16

P: n/a
On 15 juil, 21:45, Victor Bazarov <v.Abaza...@comAcast.netwrote:
courp...@gmail.com wrote:
On 15 juil, 21:10, Victor Bazarov <v.Abaza...@comAcast.netwrote:
courp...@gmail.com wrote:
On 15 juil, 18:22, James Kanze <james.ka...@gmail.comwrote:
Just ran into an interesting question concerning SFINAE. *Given
the following code:
* * #include <iostream>
* * #include <typeinfo>
* * template< typename T >
* * class P
* * {
* * public:
* * * * P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
* * * * template< typename U >
* * * * P( U* ) { std::cout << "In P: template ctor (U = "
* * * * * * * * * * << typeid( U ).name() << ')'<< std::endl ; }
* * } ;
* * class B
* * {
* * public:
* * * * virtual * * * * * * ~B() {}
* * } ;
* * int
* * main()
* * {
* * * * class D : public B {} ;
* * * * P< B p( new D ) ;
* * * * return 0 ;
* * }
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. *(My own feeling is that it shouldn't compile. *But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
In your example, a local class is used as a (deduced) template
argument. Local classes can't be used as a template argument. SFINAE
comes when type deduction fails. The C++ standard lists exhaustively
the cases where type deduction fails.
Really? *I thought that the Standard actually listed exhaustively the
cases where type deduction *succeeded*. *Everything else fails. *Where
is the exhaustive list of failures? *Just point me to the paragraph,no
need to quote it.
Explicit template argument :
14.8.2/2
Deduced template argument :
14.8.2.4 (the whole section, specifically paragraphs 11 to 18)

OK, perhaps my understanding of English is failing me, and that's why
when I see
* "If a substitution in a template parameter or in the function type
* *of the function template results in an invalid type, type deduction
* *fails."
I just conclude that this is all-inclusive rule, deduction resulting in
an invalid type fails. *The last sentence of the third bullet item in
the paragraph 2 says "Type deduction *may* fail for the following
reasons:" (with a list of the causes that can make deduction fail, the
emphasis mine). *To me it sounds too much like an extension of the note
it follows, as if it says, "Oh, BTW, here is a list of what *may* happen
among other things". *BTW, the use of "may" is very tricky AFA standards
are concerned. *There is no certainty in it when it comes to (in this
particular case) positive behavior. *It opens the door to the deduction
that is allowed to succeed in any of those enumerated situations. *For
example, the compiler is *allowed* to deduce the argument even if it
results in a pointer to a reference, if such pointer is not used...
That is probably why I never considered those lists to be exhaustive
enumerations of what is to be treated as the reason for a failure.

Yes, "may" is often the word where divergences of interpretations
start. It makes translation of standards in some other languages
tricky.

However, the section you are refering to concerns *explicit* template
arguments. In our case (deduced template argument) the relevant
section is 14.8.2.4 (specifically /11 to /18 for deduction failures),
which doesn't contain an all-inclusive rule.

Alexandre Courpron.
Jul 15 '08 #17

P: n/a
On Jul 15, 2:39*pm, James Kanze <james.ka...@gmail.comwrote:
On Jul 15, 7:54 pm, Gennaro Prota <inva...@yahoo.comwrote:


James Kanze wrote:
Just ran into an interesting question concerning SFINAE. *Given
the following code:
* * #include <iostream>
* * #include <typeinfo>
* * template< typename T >
* * class P
* * {
* * public:
* * * * P( T* ) { std::cout << "In P: normal ctor" << std::endl ; }
* * * * template< typename U >
* * * * P( U* ) { std::cout << "In P: template ctor (U = "
* * * * * * * * * * << typeid( U ).name() << ')' << std::endl ; }
* * } ;
* * class B
* * {
* * public:
* * * * virtual * * * * * * ~B() {}
* * } ;
* * int
* * main()
* * {
* * * * class D : public B {} ;
* * * * P< B p( new D ) ;
* * * * return 0 ;
* * }
Should this compile, and if so, what should the program output?
I've got three different compilers at hand: one fails to
compile, one outputs "In P: normal ctor", and one outputs "In P:
template ctor (U = ...)", where the ... is the mangled name of
D. *(My own feeling is that it shouldn't compile. *But I'm not
all that clear about template argument deduction, and at what
level SFINAE kicks in.)
First a question: was it intentional that D is a local class?

Obviously. *Otherwise, there's no reason for it not to compile,
and systematically chose the template constructor.
That's the only reason I see for it not to compile (modulo
N2580 :-)).

I'll have a glance at the paper you cite,
That should be easy since you wrote it.

Joe Cook
Jul 15 '08 #18

P: n/a
"Joe Greer"
I am far from an expert, but it seems to me that it was explicitly
instantiated with a class B (which is not local, but passed in a D *
as an
argument with a conversion to B *). I could really seeing going
either
way.
Its explicitly instantiated as so:
P< B >

The type of "new D" is unimportant in deciding the type of T. Are you
aware that there isn't implicit conversions involved in template
argument deduction? U can't be B.

Fraser.
Jul 15 '08 #19

P: n/a
Gennaro Prota wrote:
Right now I can't check the standard with the care it deserves, but
I believe it boils down to this: substitution (deduction) *succeeds*,
because "usage of local class for template type argument" isn't listed
as a case of substitution failure; so, a point of instantiation is
created for both constructors, and that leads to an error for each
constructor. This is before there's any chance to add the instantiations
to the overload set, and thus there's really no overload resolution. But
all this should be backed up with a few paragraph numbers from the
standard :-) I'll try doing that tomorrow.
Sorry, of course that's *one* error, not an error for each
constructor; I should refrain from posting at the end of a day like
yesterday was. Apart from that oversight, I think the analysis is
correct.

The text of core issue 488, which I linked to in the other post,
already mentions the relevant sections of the standard, particularly
14.8.2 [temp.deduct] par. 2 and the fact that substituting a local
type isn't listed there as a SFINAE case. (In and of itself that
section concerns the case of explicitly specified template arguments,
but then the deduced case is re-conducted back to the former). Just
let me add that we could just write

class B;

class P
{
public:
P( B * );
template< typename U >
P( U * );
} ;

class B
{
public:
virtual ~B() {}
} ;

int
main()
{
class D : public B {} ;
P p( new D ) ;
return 0 ;
}

(i.e. make P an ordinary class; who needs smart *P*ointers to be
templates :-)) and still have the same problem (in C++03):
substitution works and an ill-formed declaration is then
instantiated.

PS: VC++ should get this right with /Za. If it doesn't then this is a
regression with respect to at least VC++ 2005 (I'm assuming you are
using 2008).

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 16 '08 #20

P: n/a
On Jul 15, 11:36 pm, joseph cook <joec...@gmail.comwrote:
On Jul 15, 2:39 pm, James Kanze <james.ka...@gmail.comwrote:
[...]
First a question: was it intentional that D is a local class?
Obviously. Otherwise, there's no reason for it not to compile,
and systematically chose the template constructor.
That's the only reason I see for it not to compile (modulo
N2580 :-)).
I'll have a glance at the paper you cite,
That should be easy since you wrote it.
So it is. It also has absolutely nothing to do with the
question at hand. Since Gennaro put a smiley there, I suppose
the reference was meant as some sort of a joke. Or maybe he
simply mistyped the number.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 16 '08 #21

P: n/a
On Jul 15, 10:10 pm, Joe Greer <jgr...@doubletake.comwrote:
"Fraser Ross" <a...@zzzzzzzzzzz.comwrote in news:487cfcd5$0$5435$c3e8da3
@news.astraweb.com:
"James Kanze"
I think that the standard is clear (unless there have been
clarifications I've missed): you can't instantiate a
template over a local class.
I feel that the compiler should not attempt to instantiate with a local
class. After overload resolution failure a diagnostic might refer to
the local class. The Borland compiler gets everything right as I see
it. The Comeau compiler reports an error too soon for type deduction
failure (in broad terms) and strangely twice.
I am far from an expert, but it seems to me that it was
explicitly instantiated with a class B (which is not local,
but passed in a D * as an argument with a conversion to B *).
I could really seeing going either way.
The type of the object is clear. The problem is in the
resolution of the call to the constructor, and how type
deduction works.

From other postings, I gather that the committee has fixed this
by allowing instantiation over local types. And I gather that
there was more or less a consensus in the committee (or at least
those members addressing the issue), that the code was illegal
according to the original standard.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 16 '08 #22

P: n/a
James Kanze wrote:
[...]
>>>That's the only reason I see for it not to compile (modulo
N2580 :-)).
>>I'll have a glance at the paper you cite,
>That should be easy since you wrote it.

So it is. It also has absolutely nothing to do with the
question at hand. Since Gennaro put a smiley there, I suppose
the reference was meant as some sort of a joke. Or maybe he
simply mistyped the number.
Yes, it was meant as a joke (sorry if it failed to be so), since
you've longly been in the (correct) habit of including both <ostream>
and <iostreamin such cases.

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 16 '08 #23

P: n/a
Is a local class in any way less hidden with the changes? I have seen
one described as final because outside code cannot derive from one. Is
this still the case? I would think not.

Fraser.
Jul 16 '08 #24

P: n/a
"Fraser Ross" <a@zzzzzzzzzzz.comwrote in news:487d2798$0$5408$c3e8da3
@news.astraweb.com:
"Joe Greer"
>I am far from an expert, but it seems to me that it was explicitly
instantiated with a class B (which is not local, but passed in a D *
as an
>argument with a conversion to B *). I could really seeing going
either
>way.

Its explicitly instantiated as so:
P< B >

Well, from my point of view (which admittedly is probably wrong because
it is so simple and understandable) once you have explicitly
instantiated a template with something like P<Bit should be as if the
class were really as if you had written:

class P
{
public:
P(B *) {}
//etc
}

And type deductions would be based off of type B. That is, the actual
type passed into the constructor no longer matters because you
explicitly told it the base class.
The type of "new D" is unimportant in deciding the type of T. Are you
aware that there isn't implicit conversions involved in template
argument deduction? U can't be B.
I don't think I even implied that the conversion had anything to do with
argument deduction.
joe
Jul 16 '08 #25

P: n/a
On Jul 16, 2:28 pm, "Fraser Ross" <a...@zzzzzzzzzzz.comwrote:
Is a local class in any way less hidden with the changes? I
have seen one described as final because outside code cannot
derive from one. Is this still the case? I would think not.
It never was the case, as far as I know.

I don't think that local classes, per se, will change much.
What is being changed is that at least in some cases, you will
be allowed to instantiate a template with a name which doesn't
have external linkage.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 16 '08 #26

P: n/a
On Jul 16, 2:33 pm, Joe Greer <jgr...@doubletake.comwrote:
"Fraser Ross" <a...@zzzzzzzzzzz.comwrote in news:487d2798$0$5408$c3e8da3
@news.astraweb.com:
"Joe Greer"
I am far from an expert, but it seems to me that it was
explicitly instantiated with a class B (which is not local,
but passed in a D * as an argument with a conversion to B
*). I could really seeing going either way.
Its explicitly instantiated as so:
P< B >
Well, from my point of view (which admittedly is probably
wrong because it is so simple and understandable) once you
have explicitly instantiated a template with something like
P<Bit should be as if the class were really as if you had
written:
class P
{
public:
P(B *) {}
//etc
}
And type deductions would be based off of type B. That is,
the actual type passed into the constructor no longer matters
because you explicitly told it the base class.
There were two constructors, one of which was a member template.
So the compiler has to do overload resolution to choose the
constructor, and template argument deduction on the member
template to know which one it includes in the overload set.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 16 '08 #27

P: n/a
On 16 juil, 09:13, Gennaro Prota <inva...@yahoo.comwrote:
Gennaro Prota wrote:
Right now I can't check the standard with the care it deserves, but
I believe it boils down to this: substitution (deduction) *succeeds*,
because "usage of local class for template type argument" isn't listed
as a case of substitution failure; so, a point of instantiation is
created for both constructors, and that leads to an error for each
constructor. This is before there's any chance to add the instantiations
to the overload set, and thus there's really no overload resolution. But
all this should be backed up with a few paragraph numbers from the
standard :-) I'll try doing that tomorrow.

Sorry, of course that's *one* error, not an error for each
constructor; I should refrain from posting at the end of a day like
yesterday was. Apart from that oversight, I think the analysis is
correct.

The text of core issue 488, which I linked to in the other post,
already mentions the relevant sections of the standard, particularly
14.8.2 [temp.deduct] par. 2 and the fact that substituting a local
type isn't listed there as a SFINAE case. (In and of itself that
section concerns the case of explicitly specified template arguments,
but then the deduced case is re-conducted back to the former).
There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.

Alexandre Courpron.
Jul 16 '08 #28

P: n/a
co******@gmail.com wrote:
[...]
>The text of core issue 488, which I linked to in the other post,
already mentions the relevant sections of the standard, particularly
14.8.2 [temp.deduct] par. 2 and the fact that substituting a local
type isn't listed there as a SFINAE case. (In and of itself that
section concerns the case of explicitly specified template arguments,
but then the deduced case is re-conducted back to the former).

There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.
Did you mean 14.8.2/4?

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 16 '08 #29

P: n/a
On 16 juil, 22:52, Gennaro Prota <inva...@yahoo.comwrote:
courp...@gmail.com wrote:

* * *[...]
The text of core issue 488, which I linked to in the other post,
already mentions the relevant sections of the standard, particularly
14.8.2 [temp.deduct] par. 2 and the fact that substituting a local
type isn't listed there as a SFINAE case. (In and of itself that
section concerns the case of explicitly specified template arguments,
but then the deduced case is re-conducted back to the former).
There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.

Did you mean 14.8.2/4?
No, I mean 14.8.2.4 [temp.deduct.type] (the whole section).

Alexandre Courpron.
Jul 16 '08 #30

P: n/a
co******@gmail.com wrote:
>>There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.
Did you mean 14.8.2/4?

No, I mean 14.8.2.4 [temp.deduct.type] (the whole section).
Hmm, yes, I think you are right. It's odd that the text of the core
issue gets it wrong, though, even in the proposed resolution from
October 2005!

Thanks for making me notice this.

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 16 '08 #31

P: n/a
On 16 juil, 23:36, Gennaro Prota <inva...@yahoo.comwrote:
courp...@gmail.com wrote:
>There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.
Did you mean 14.8.2/4?
No, I mean 14.8.2.4 [temp.deduct.type] (the whole section).

Hmm, yes, I think you are right. It's odd that the text of the core
issue gets it wrong, though, even in the proposed resolution from
October 2005!

Thanks for making me notice this.
You're welcome.

I guess that core issues reports can also have issues.

In the C++ standard, the whole section about templates and argument
deduction uses maybe confusing terminology.
For instance, consider the following example :

****
template < class T >
void f( T ) {}

....

f(0); // #1
f<int>(0); // #2
****

Usually, one could say that there is a template argument deduction in
the first call and not the second one. This is the natural and popular
meaning of "argument deduction", used in many technical papers, books,
faqs, etc...

However, there is actually a template argument deduction for both
calls. For the second call, argument deduction just does nothing and
*succeeds*. If the explicit template arguments had led to an invalid
type as described in 14.8.2/2, then the deduction would have just
*failed*. That's why SFINAE can occur when there is only an explicit
specification of template arguments ( since SFINAE comes when argument
deduction fails ). I insist on the fact that argument deduction exists
in the second call, but does nothing : that means that there is no
*deduced* template argument from that call, while there is one deduced
template argument from the first call. That's why the core issue #250
was arised for example :
http://www.open-std.org/jtc1/sc22/wg...fects.html#250
The standard says that (14.8.2/2) :
"When an explicit template argument list is specified, the template
arguments must be compatible with the template parameter list and must
result in a valid function type as described below; otherwise type
deduction fails."

That seems contradictory only if you use the popular (and inaccurate)
meaning of "deduction". The core issue 488 took that sentence without
the first part ("When an explicit template argument list is
specified"). That omission led to a wrong assumption.
Alexandre Courpron.
Jul 17 '08 #32

P: n/a
co******@gmail.com wrote:
On 16 juil, 23:36, Gennaro Prota <inva...@yahoo.comwrote:
>courp...@gmail.com wrote:
>>>>There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.
Did you mean 14.8.2/4?
No, I mean 14.8.2.4 [temp.deduct.type] (the whole section).
Hmm, yes, I think you are right. It's odd that the text of the core
issue gets it wrong, though, even in the proposed resolution from
October 2005!

Thanks for making me notice this.

You're welcome.

I guess that core issues reports can also have issues.

In the C++ standard, the whole section about templates and argument
deduction uses maybe confusing terminology.
For instance, consider the following example :

****
template < class T >
void f( T ) {}

...

f(0); // #1
f<int>(0); // #2
****

Usually, one could say that there is a template argument deduction in
the first call and not the second one. This is the natural and popular
meaning of "argument deduction", used in many technical papers, books,
faqs, etc...

However, there is actually a template argument deduction for both
calls. For the second call, argument deduction just does nothing and
*succeeds*. If the explicit template arguments had led to an invalid
type as described in 14.8.2/2, then the deduction would have just
*failed*. That's why SFINAE can occur when there is only an explicit
specification of template arguments ( since SFINAE comes when argument
deduction fails ).
Wait a minute! This just does not sound right. Consider this

template<class Tvoid foo(T, char T::* = 0);
void foo(int);
...
foo(0);

What would happen? Deducing T (for the first 'foo') as 'int' (which is
what I'd expect) means that the second argument is invalid since 'int
does not have members. Isn't this a case of SFINAE? (that's a
rhetorical question). Well, there is no explicit specification of the
template arguments for 'foo' in the expression that requires type
deduction, is there?

Looking forward to your comments.
I insist on the fact that argument deduction exists
in the second call, but does nothing : that means that there is no
*deduced* template argument from that call, while there is one deduced
template argument from the first call. That's why the core issue #250
was arised for example :
http://www.open-std.org/jtc1/sc22/wg...fects.html#250
The standard says that (14.8.2/2) :
"When an explicit template argument list is specified, the template
arguments must be compatible with the template parameter list and must
result in a valid function type as described below; otherwise type
deduction fails."

That seems contradictory only if you use the popular (and inaccurate)
meaning of "deduction". The core issue 488 took that sentence without
the first part ("When an explicit template argument list is
specified"). That omission led to a wrong assumption.
Alexandre Courpron.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 17 '08 #33

P: n/a
On 17 juil, 16:55, Victor Bazarov <v.Abaza...@comAcast.netwrote:
courp...@gmail.com wrote:
[...]
****
template < class T >
void f( T ) {}
...
f(0); // *#1
f<int>(0); // #2
****
Usually, one could say that there is a template argument deduction in
the first call and not the second one. This is the natural and popular
meaning of "argument deduction", used in many technical papers, books,
faqs, etc...
However, there is actually a template argument deduction for both
calls. For the second call, argument deduction just does nothing and
*succeeds*. If the explicit template arguments had led to an invalid
type as described in 14.8.2/2, then the deduction would have just
*failed*. That's why SFINAE can occur when there is only an explicit
specification of template arguments ( since SFINAE comes when argument
deduction fails ).

Wait a minute! *This just does not sound right. *Consider this

* * *template<class Tvoid foo(T, char T::* = 0);
* * *void foo(int);
* * *...
* * *foo(0);

What would happen? *Deducing T (for the first 'foo') as 'int' (which is
what I'd expect) means that the second argument is invalid since 'int
does not have members. *Isn't this a case of SFINAE? *(that's a
rhetorical question). *Well, there is no explicit specification of the
template arguments for 'foo' in the expression that requires type
deduction, is there?

Looking forward to your comments.
Yes, there is SFINAE here and that doesn't invalidate what I said.

I said that SFINAE *can* occur when there is only an explicit
specification of template arguments. That doesn't mean it can't occur
when there is no explicit template arguments. Note that I used the
word "only" here to say that there is "only" explicit template
arguments (without any deduced argument). I didn't mean that "SFINAE
can *only* occur when there is only an explicit specification of
template arguments".

Now why am I talking about explicit template arguments specifically ?
Because SFINAE comes when *argument deduction fails*. The important
terms here are "argument deduction". From that definition, it is
obvious that SFINAE can be applied when deduced template arguments are
involved. But SFINAE can also apply when there are only explicit
template arguments (and no deduced argument at all) and that is not
obvious because, from a popular conception, there is no argument
deduction with explicit template arguments only (which is wrong).

Alexandre Courpron.

Jul 17 '08 #34

P: n/a
Gennaro Prota wrote:
co******@gmail.com wrote:
>>>There is no reconduction. 14.8.2/2 concerns explicit template
arguments only.
Contrary to what the core issue 488 says, the relevant section is
14.8.2.4.
Did you mean 14.8.2/4?

No, I mean 14.8.2.4 [temp.deduct.type] (the whole section).

Hmm, yes, I think you are right. It's odd that the text of the core
issue gets it wrong, though, even in the proposed resolution from
October 2005!

Thanks for making me notice this.
As I said, Alexandre's reading of the standard seemed pretty correct
to me. So, I wrote to William M. Miller to point out that, apparently,
the text of core issue 488 was referencing the wrong section of the
standard. Here is a summary of the resulting conversation; the text is
slightly edited and reordered to make it (hopefully) clearer in the
context of this thread, and is being forwarded to the newsgroup with
his permission.

WM:
The reference to 14.8.2 in issue 488 is there because the list of
things that cause deduction failures is in 14.8.2 (or, rather, it was;
the version of the Standard incorporating paper N2657 no longer has a
normative list of deduction failures). There was no implication that
14.8.2 was the place that described how template argument deduction
was done for this particular example.

Me:
The problem is, IIUC, that there's no such a thing as "*the* list of
things that cause deduction failures"; but there is "the list of
things that cause deduction failures *when there's an explicit
template argument list*".

WM:
The presentation in 14.8.2 and subsections has always been somewhat
problematic. It's complicated trying to relate all the contexts in
which template argument deduction is done and all the ways a given
template argument value is obtained (explicitly specified, deduced, or
via a default argument), and the fact that the specification evolved,
rather than being written all at once, also contributed to the general
lack of clarity.

However, the intent behind the statement "Type deduction may fail for
the following reasons" was that the following list of bullets applied
regardless of whether the template arguments were deduced, explicitly
specified, or from a default template argument. (That also applies to
the second bullet in that list [not sub-bullet], requiring that a
non-type template argument be convertible to the type of the
corresponding parameter.) The idea was that you first substitute any
explicit template arguments, then attempt to deduce the remaining
arguments as specified in the subsections of 14.8.2, then use default
arguments for any parameters that do not have arguments at that point.
If, after substituting all the arguments for uses of parameters in the
declaration, you end up with a type that is invalid for any of the
reasons in 14.8.2p2, deduction fails for that particular
specialization.

You can see that intention reflected, although imperfectly, in the
wording of 14.8.2p5 (in the current WP; it was similar in the
Standard, but missing the mention of default arguments):

When all template arguments have been deduced or obtained
from default template arguments, all uses of template
parameters in non-deduced contexts are replaced with the
corresponding deduced or default argument values. If the
substitution results in an invalid type, as described above,
type deduction fails.

Note the explicit linking of deduced and default arguments, not just
explicitly-specified arguments, with the reference to p2 ("as
described above").

I think the revision in the current WP makes this clearer, although
I'm sure that further improvement is possible.

Me:
Ah, thanks! [...] I had noticed the wording in 14.8.2p5; in fact
here's what I wrote on c.l.c++.m:

The text of core issue 488, which I linked to in the other
post, already mentions the relevant sections of the standard,
particularly 14.8.2 [temp.deduct] par. 2 [snip...] (In and of
itself that section concerns the case of explicitly specified
template arguments, but then the deduced case is re-conducted
back to the former).

When I say "re-conducted back" I'm thinking exactly of the "as
described above" which you cite. But A. Courpron replied to that with

There is no reconduction. 14.8.2/2 concerns explicit template
arguments only. Contrary to what the core issue 488 says, the
relevant section is 14.8.2.4.

and, upon strict reading, he seemed to be correct.

WM:
I hadn't thought about it from that perspective, but it's easy to see
how one would reach that conclusion from the existing wording. That
does appear to be what it's saying, but that was not the intent.
Hopefully things will be better with the current revision.

--
Gennaro Prota | <https://sourceforge.net/projects/breeze/>
Do you need expertise in C++? I'm available.
Jul 17 '08 #35

P: n/a
On 17 juil, 23:35, Gennaro Prota <inva...@yahoo.comwrote:
Gennaro Prota wrote:
[...]

As I said, Alexandre's reading of the standard seemed pretty correct
to me. So, I wrote to William M. Miller to point out that, apparently,
the text of core issue 488 was referencing the wrong section of the
standard. Here is a summary of the resulting conversation; the text is
slightly edited and reordered to make it (hopefully) clearer in the
context of this thread, and is being forwarded to the newsgroup with
his permission.

WM:
The reference to 14.8.2 in issue 488 is there because the list of
things that cause deduction failures is in 14.8.2 (or, rather, it was;
the version of the Standard incorporating paper N2657 no longer has a
normative list of deduction failures). *There was no implication that
14.8.2 was the place that described how template argument deduction
was done for this particular example.

Me:
The problem is, IIUC, that there's no such a thing as "*the* list of
things that cause deduction failures"; but there is "the list of
things that cause deduction failures *when there's an explicit
template argument list*".

WM:
The presentation in 14.8.2 and subsections has always been somewhat
problematic. *It's complicated trying to relate all the contexts in
which template argument deduction is done and all the ways a given
template argument value is obtained (explicitly specified, deduced, or
via a default argument), and the fact that the specification evolved,
rather than being written all at once, also contributed to the general
lack of clarity.

However, the intent behind the statement "Type deduction may fail for
the following reasons" was that the following list of bullets applied
regardless of whether the template arguments were deduced, explicitly
specified, or from a default template argument. *(That also applies to
the second bullet in that list [not sub-bullet], requiring that a
non-type template argument be convertible to the type of the
corresponding parameter.) *The idea was that you first substitute any
explicit template arguments, then attempt to deduce the remaining
arguments as specified in the subsections of 14.8.2, then use default
arguments for any parameters that do not have arguments at that point.
If, after substituting all the arguments for uses of parameters in the
declaration, you end up with a type that is invalid for any of the
reasons in 14.8.2p2, deduction fails for that particular
specialization.

You can see that intention reflected, although imperfectly, in the
wording of 14.8.2p5 (in the current WP; it was similar in the
Standard, but missing the mention of default arguments):

* *When all template arguments have been deduced or obtained
* *from default template arguments, all uses of template
* *parameters in non-deduced contexts are replaced with the
* *corresponding deduced or default argument values. *If the
* *substitution results in an invalid type, as described above,
* *type deduction fails.

Note the explicit linking of deduced and default arguments, not just
explicitly-specified arguments, with the reference to p2 ("as
described above").

I think the revision in the current WP makes this clearer, although
I'm sure that further improvement is possible.

Me:
Ah, thanks! [...] I had noticed the wording in 14.8.2p5; in fact
here's what I wrote on c.l.c++.m:

* The text of core issue 488, which I linked to in the other
* post, already mentions the relevant sections of the standard,
* particularly 14.8.2 [temp.deduct] par. 2 [snip...] (In and of
* itself that section concerns the case of explicitly specified
* template arguments, but then the deduced case is re-conducted
* back to the former).

When I say "re-conducted back" I'm thinking exactly of the "as
described above" which you cite. But A. Courpron replied to that with

* There is no reconduction. 14.8.2/2 concerns explicit template
* arguments only. Contrary to what the core issue 488 says, the
* relevant section is 14.8.2.4.

and, upon strict reading, he seemed to be correct.

WM:
I hadn't thought about it from that perspective, but it's easy to see
how one would reach that conclusion from the existing wording. *That
does appear to be what it's saying, but that was not the intent.
Hopefully things will be better with the current revision.
Very interesting.

However, consider the following 2 points :

1) About 14.8.2/5 (~ 14.8.2/4 in the current standard) :
This paragraph concerns the substitution of deduced template arguments
in nondeduced contexts only.

Example :
****
template < class T >
struct foo { typedef T type; };

template < class T >
void f( T arg1, typename foo<T>::type arg2) {}
...

f(0,0);
****

Here the function specialization is done in 2 steps :
- template argument deduction, done for arg1 only
- substitution of T for arg2

Between these 2 steps, T is known. But, for step 2, it doesn't matter
where T comes from, it can be a deduced argument or an explicit
argument, it should not matter. That's why it's perfectly logical that
"substitution of deduced template arguments in nondeduced contexts"
follows the same rules as "substitution of explicit template
arguments". There is no reconduction of the whole process of template
argument deduction here.
2) Explicit template arguments can't have the same list of type
deduction failures than deduced template arguments, because failures
in one context are meaningless in the another. Here is one example of
type deduction failure for explicit arguments :

Attempting to use a type that is not a class type in a qualified name.
[Example:
template <class Tint f(typename T::B*);
int i = f<int>(0);
]

You can see that template argument deduction can't lead to such case,
except for the "substitution of deduced template arguments in
nondeduced contexts" described in 14.8.2/4. All of this is currently
very logical to me.

The core issue 488 clearly doesn't fall in the list of type deduction
failures for explicit template arguments.

Alexandre Courpron.
Jul 18 '08 #36

This discussion thread is closed

Replies have been disabled for this discussion.