Re: Explicitly specializing std::min() on VC++ 2005 Express Edition
Matthias Hofmann wrote:
"Tom Widmer [VC++ MVP]" <tom_usenet@hotmail.com> schrieb im Newsbeitrag
news:eyKHNIkjHHA.4872@TK2MSFTNGP03.phx.gbl...
template <std::size_t N>
struct min_impl<char[N]>;
This specialization for non-const char[N] seems superflous, as
minimum<T>() accepts a constant reference, so min_impl<T>::impl() will
never see a non-const char[N].
Good point, though I think it's the other way around - char[N] is needed,
but not char const[N].
When I spezialize as follows:
// Non-const version.
template <std::size_t N>
struct minimum_impl<char[N]>;
then I get an error when calling minimum_impl::impl<>() from within
minimum<>() because the actual parameters are constant.
Interesting, I just tried that on VC2005 and got an error too (the same
error?). The error is due to a compiler bug, but there's a very simple
workaround, which is to drop the use of ?:.
template <std::size_t N>
struct minimum_impl<char[N]>
{
static char const (&impl(char const (&a)[N], char const (&b)[N]))[N]
{ if (std::strcmp(a, b) < 0)
return a;
return b; }
};
The problem is that VC2005 is ignoring 5.16/4 (about ?: ):
"If the second and third operands are lvalues and have the same type,
the result is of that type and is an lvalue."
Instead, it is applying the lvalue-to-rvalue conversion (e.g. the
array-to-pointer conversion) to the parts of the ?: expression, and thus
trying to return a pointer where an array is expected.
On the other hand,
when I spezialize as follows:
// Const version.
template <std::size_t N>
struct minimum_impl<const char[N]>;
then the primary template is called, not the specialization. I found out
that this is because there is a bug in your implementation of minimum<>().
It should look like this:
template <class T> inline
const T& minimum( const T& a, const T& b )
{
// Note the 'const' before 'T'.
return minimum_impl<const T>::impl( a, b );
}
That's a bit of an odd way of doing it, since it means you always have
to specialize for const T, rather than T, as is usual.
C++ declaration syntax isn't very nice.
Indeed, that's why I am trying a typedef:
template <std::size_t N>
struct minimum_impl<const char[N]>
{
typedef const char( &ARG )[N];
static ARG impl( ARG a, ARG b )
{
// C2440 error here.
return std::strcmp( a, b ) < 0 ? a : b;
}
};
That looks much clearer, but on VC++ 2005 Express Edition, it does not work.
I get a C2440 error for not being able to convert from 'const char*' to
'const char (&)[2]' in the return statement. Where does that 'const char*'
come from?
See above. Just to be clear, this compiles and works on VC2005:
#include <cstring>
template <class T>
struct minimum_impl
{
static const T& impl(const T& a, const T& b);
// { return a < b ? a : b; } commented out to check correct one is
//being called.
};
template <std::size_t N>
struct minimum_impl<char[N]>
{
static char const (&impl(char const (&a)[N], char const (&b)[N]))[N]
{ if (std::strcmp(a, b) < 0)
return a;
return b; }
};
template <class T> inline
const T& minimum( const T& a, const T& b )
{
// Note no 'const' before 'T'.
return minimum_impl<T>::impl( a, b );
}
int main()
{
char foo[] = "hello";
char bar[] = "world";
minimum(foo, bar);
}
Tom