Topic: Template expansion during function overload resolution in C++0= X
Author: Paul Bibbings <paul.bibbings@gmail.com>
Date: Fri, 11 Jun 2010 16:33:43 CST Raw View
Jesse Perla <jesseperla@gmail.com> writes:
> I encountered a "bug" in a few compilers when doing some meta-
> programming to choose return types. On Intel 11.1 and MSVC10 the
> compiler was doing a template expansion of the return types even for
> overloads that don't match. Here is a minimalist example of the
> problem.
> ///////////////////
> template <class T> struct Z {
> typedef T::x type;
> };
> template <class T> Z<T>::type f(void *, T);
> template <class T> void f(int, T);
> struct A {} a;
> int main() {
> f(1, a); // Works on GCC,
> // Fails on pre-fixed Intel 11.1 and MSVC10
> // because Z template expanded for unused overload
> }
> /////////////////
> Intel has described a fix in:
> http://software.intel.com/en-us/forums/showpost.php?p=120228
>
> Microsoft has said that this is by design according to the latest C+
> +0X standard:
>
https://connect.microsoft.com/VisualStudio/feedback/details/560886/bug-in-t=
emplate-expansion-during-function-overload-resolution?wa=wsignin1.0#tabs
>
> Microsoft's reasoning is: This is a grey area that the C++03 standard
> does not address; however, the most current draft (n3090) of the C++0x
> standard (14.9.2/8) states the following:
> "Only invalid types and expressions in the immediate context of the
> function type and its template parameter types can result in a
> deduction failure." Therefore, this behavior is by-design
I am not an expert in this area. Nevertheless, my attention is drawn to
N3092:[dcl.fct] =A78.3.5/6 where it states:
"The return type, the parameter-type-list, the /ref-qualifier/, and
the /cv-qualifier-seq/, but not the default arguments (8.3.6) or the
exception specification (15.4), are part of the function type."
To me this would suggest that the return type is part of the "immediate
context of the function type" in being "part of the function type" and,
as such, invalid types in the return type "can result in deduction
failure." I don't know if this is a correct interpretation or not, but
Microsoft certainly don't read it this way since they justify their
"Therefore, this behavior is by-design" by adding - immediately after
the extract you quote - "Since a deduction failure does *not* occur, the
program is considered ill-formed and an error is emitted." [my emphasis]
Regards
Paul Bibbings
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Fri, 11 Jun 2010 16:34:00 CST Raw View
On 10 Jun., 22:06, Jesse Perla <jessepe...@gmail.com> wrote:
> I encountered a "bug" in a few compilers when doing some meta-
> programming to choose return types. On Intel 11.1 and MSVC10 the
> compiler was doing a template expansion of the return types even for
> overloads that don't match. Here is a minimalist example of the
> problem.
> ///////////////////
> template <class T> struct Z {
> typedef T::x type;
I assume that should say
typedef typename T::x type;
> };
> template <class T> Z<T>::type f(void *, T);
Again, I assume that should say
template <class T> typename Z<T>::type f(void *, T);
> template <class T> void f(int, T);
> struct A {} a;
> int main() {
> f(1, a); // Works on GCC,
> // Fails on pre-fixed Intel 11.1 and MSVC10
> // because Z template expanded for unused overload
> }
> /////////////////
> Intel has described a fix in:
http://software.intel.com/en-us/forums/showpost.php?p=120228
>
> Microsoft has said that this is by design according to the latest C+
> +0X standard:
https://connect.microsoft.com/VisualStudio/feedback/details/560886/bu...
>
> Microsoft's reasoning is: This is a grey area that the C++03 standard
> does not address; however, the most current draft (n3090) of the C++0x
> standard (14.9.2/8) states the following:
> "Only invalid types and expressions in the immediate context of the
> function type and its template parameter types can result in a
> deduction failure." Therefore, this behavior is by-design
>
> Is this a correct interpretation of the standard?
This describes the situation quite well, even though I would *not*
describe the situation as a "grey area". Note, that actually
*two* different effects are coming together:
1) According to the FCD wording (14.8.2 [temp.deduct]/8):
"[..] Only invalid types and expressions in the immediate context
of the function type and its template parameter types can result
in a deduction failure. [ Note: The evaluation of the substituted
types and expressions can result in side effects such as the
instantiation of class template specializations and/or function
template specializations, the generation of implicitly-defined
functions, etc. Such side effects are not in the =93immediate context=94
and can result in the program being ill-formed.=97end note ]"
And indeed, in your corrected example (in regard to the two
missing typename usages) the invalid type does *not*
occur in the *immediate context*, because it happens within
the definition of the template Z - this is an important difference!
Now you may wonder: Why is this example well-formed for
some compilers? Well, this is *not* due silent deduction
failure in those (at least, it shouldn't be), instead it is due to a
freedom given to implementations in this situation, where we
finally come to the second effect:
2) According to 14.7.1 [temp.inst]/5:
"If the overload resolution process can determine the correct
function to call without instantiating a class template definition,
it is unspecified whether that instantiation actually takes place.
[ Example:
template <class T> struct S {
operator int();
};
void f(int);
void f(S<int>&);
void f(S<float>);
void g(S<int>& sr) {
f(sr); // instantiation of S<int> allowed but not required
// instantiation of S<float> allowed but not required
};
=97end example ]"
So whether a compiler recognizes that the overload
template <class T> typename Z<T>::type f(void *, T);
cannot be the correct function selected, is purely a
matter of quality of implementation. Theoretically, the
compiler can "know" this here, because the literal 1 is
not a null pointer constant and thus cannot be
implicitly converted to void*.
> Can a bug to the standard be submitted to cover this case?
> I think that this pattern can come up all over the place in
> metaprogramming.
IMO in general it would be a very strong requirement to
fix arbitrarily deep instantiation errors. Note that it is very
easy to create much more complex examples by constructing
even more indirections into the definition of Z. At least
the current situation is clearly defined. I consider it as
correct that the case described in [temp.inst]/5 is
depending on the compiler quality, so you should not
rely on it in portable programming.
HTH & Greetings from Bremen,
Daniel Kr=FCgler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]