From: Daniel Krügler on
On 11 Jun., 03:13, Jesse Perla <jessepe...(a)gmail.com> wrote:
> (I attempted to post this to comp.std.c++ but it didn't seem to pass
> moderation, so I thought this is a 2nd best location)

I replied to your question on comp.std.c++, but it is not visible
at the time of writing this. Since already one of my further
replies seems to get lost, I retry here.

> While doing some meta-programming for choosing a return type of an
> overloaded function, I ran into a problem with the expansion of
> templates in the return type of overloaded functions (that are
> rejected in the overload for other reasons). A version at the bottom
> is attached, though a link on intel simplifies the problem a little.
>
> The code works fine in g++/MinGW (I am using 4.4).
>
> However, on Intel 11.1 and MSVC10, it fails since it tries to
> instantiate the my_meta1<> even though the overload isn't used.

This is conforming, because it is unspecified whether the
program is well-formed or ill-formed (see below).

> Intel responded that the standard was a little gray here but they are
> making the following change:http://software.intel.com/en-us/forums/showpost.php?p=120228
>
> Microsoft on the other hand says that this is by design:
> " 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. Since a deduction failure does
> not occur, the program is considered ill-formed and an error is
> emitted."
> (Seehttp://connect.microsoft.com/VisualStudio/feedback/details/560886/bug...
> )
>
> Do you think the Microsoft interpretation is correct? If so, is this
> a 'bug' in the current C++0X standard? I think that the pattern above
> comes up frequently in meta-programming where return types become
> complicated and the only way around it seems to be some godawful
> metaprogramming.

I don't think that it is correct to nominate it as "grey area". The
C++0x FCD wording makes it quite clear in 14.8.2 [temp.deduct]/8
that your example does not fall into "silent deduction failure",
because the invalid type does not occur in the immediate context
of the function type. So the program you presented could be
ill-formed on any compiler.

The fact that your code is well-formed on some compilers is not
(at least it should not) due to the fact that the compiler considers
this as "extended SFINAE". Such compiler takes advantage of
the freedom given to implementations described in 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.
[..]"

So, for those compilers who accept your program, they don't
instantiate

template<typename DataType>
typename my_meta1<DataType>::value
f(tag1, DataType data)

because they immediately recognize that no conversion exists from
tag2 to tag1 to make that it valid candidate.

Personally I consider the FCD wording as clear and sufficient
at least for C++0x. Extending SFINAE cases beyond that
would put extremely strong requirements on implementations to
be able to handle arbitrary deep instantiation errors within
templates. It is rather easy, to construct a more more complicated
example out of yours by adding further indirections into your class
template with the same result and it is also possible to solve your
problem in a portable way. The latter requires a bit more
"hand-writing", but it's doable. A possible workaround is to define
a trait template has_value and to use classical enable_if techniques
to filter the unwanted overload away. So, instead of writing

template<typename T>
struct my_meta1
{
typedef typename T::value value;
};

you could write

template <typename T>
struct has_value {
template <typename> struct wrapper {};
template <typename U> static char test(wrapper<typename U::value>*);
template <typename> static char (&test(...))[2];
static const bool value = sizeof(test<T>(0)) == 1;
};

template<typename, bool = has_value<T>::value>
struct my_meta1 {};

template<typename T>
struct my_meta1<T, true>
{
typedef typename T::value value;
};

HTH & Greetings from Bremen,

Daniel Kr�gler


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