Topic: Template template parameters and function template overload resolution
Author: "Anthony Williams"<anthwil@nortelnetworks.com>
Date: Tue, 19 Mar 2002 19:31:47 GMT Raw View
Inspired by the recent discussion on comp.lang.c++.moderated regarding
is_enum, I attempted to write a class to check whether a type contains a
member template. This relies on a helper check function, and a class
template
typedef char Small;
struct Big{ Small dummy[2];};
template<template<typename T> class X>
struct TemplateChecker1;
template<typename T>
Small check(TemplateChecker1<T::template MemberTemplate>* );
template<typename T>
Big check(...);
template<typename T>
struct HasNestedTemplate
{
enum{result=sizeof(check<T>(0))==sizeof(Small)};
};
This works fine, unless the member template takes more than one template
parameter, in which case it gives an error about mismatch of template
arguments for TemplateChecker1 e.g. with
struct X
{
template<typename T,typename U>
struct MemberTemplate{};
};
using HasNestedTemplate<X>::result gives a compilation error.
I tried adding
template<template<typename T,typename U> class X>
struct TemplateChecker2;
template<typename T>
Small check(TemplateChecker2<T::template MemberTemplate>* );
On the premise that the version with TemplateChecker1 would be invalid, so
ignored. However Comeau C++ online reports an overloading ambiguity between
the different versions of check.
My question therefore is:
Is Comeau C++ correct to report an ambiguity, or does the mismatch between
X::MemberTemplate and TemplateChecker1 mean that the corresponding version
of check() is discarded, and only check(TemplateChecker2< ...>*) is
considered (In which case the original case shouldn't be an error either ---
the check(...) version should be taken in preference)?
I have read and re-read section 14.8, and can't find anything that makes it
clear to me one way or the other, though my gut feeling is that it should be
valid code, and not ambiguous.
Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "leavings" <leavings@attbi.com>
Date: Wed, 20 Mar 2002 01:10:08 GMT Raw View
"Anthony Williams" <anthwil@nortelnetworks.com> wrote in message
news:a77fm7$9r0$1@bcarh8ab.ca.nortel.com...
> Inspired by the recent discussion on comp.lang.c++.moderated regarding
> is_enum, I attempted to write a class to check whether a type contains a
> member template. This relies on a helper check function, and a class
> template
[snip]
I tried this several different ways to get around what I think should already
work. The first one actually works correctly on Comeau C++. The other two
choke to death on one thing or another.
typedef char small_t;
struct large_t {
char unused[256];
};
struct null_t;
template<class> struct split;
template< template<class> class T, class T1 >
struct split< T<T1> > {
struct type { };
};
template< template<class, class> class T, class T1, class T2 >
struct split< T<T1, T2> > {
struct type { };
};
template< template<class, class, class> class T, class T1, class T2, class T3 >
struct split< T<T1, T2, T3> > {
struct type { };
};
// etc.
template<class T> class has_template_NAME {
private:
template<class U>
static small_t check(
typename split<
typename U::template NAME<null_t>
>::type*
);
template<class U>
static small_t check(
typename split<
typename U::template NAME<null_t, null_t>
>::type*
);
template<class U>
static small_t check(
typename split<
typename U::template NAME<null_t, null_t, null_t>
>::type*
);
// etc.
template<class U> static large_t check(...);
public:
static const bool value = sizeof(check<T>(0)) == sizeof(small_t);
};
template<class T> const bool has_template_NAME<T>::value;
struct X {
struct NAME { }; // non-template
};
struct Y {
template<class> struct NAME { }; // 1 args
};
struct Z {
template<class, class> struct NAME { }; // 2 args
};
int main(int argc, char* argv[]) {
std::cout
<< has_template_NAME<X>::value << '\n'
<< has_template_NAME<Y>::value << '\n'
<< has_template_NAME<Z>::value << &std::endl;
return 0;
}
It took me an hour to try to get around weird compiler errors trying it various
ways. I initially tried this (which I think should work)...
template<class T> class has_template_NAME {
private:
template<class U>
static small_t check(typename U::template NAME<null_t>*);
template<class U>
static small_t check(typename U::template NAME<null_t, null_t>*);
template<class U>
static small_t check(typename U::template NAME<null_t, null_t,
null_t>*);
// etc.
template<class U> static large_t check(...);
public:
static const bool value = sizeof(check<T>(0)) == sizeof(small_t);
};
// use structs X, Y, and Z from above
int main(int argc, char* argv[]) {
std::cout
<< has_template_NAME<X>::value << '\n' // choke to death here
<< has_template_NAME<Y>::value << '\n'
<< has_template_NAME<Z>::value << &std::endl;
return 0;
}
This works fine (on Comeau C++) as long as no type that you pass has a
non-template type named NAME. In which case, Comeau says that it can't choose
between which 'equally valid' overloads in the 'call' to check (the "supposed"
valid overloads are all non-ellipsis functions--which is completely
nonsensical). I think that Comeau is in error here. So I tried this instead:
template<int I> struct param_count {
char value[I];
};
template<template<class> class T> param_count<1> binding();
template<template<class, class> class T> param_count<2> binding();
template<template<class, class, class> class T> param_count<3> binding();
// etc.
template<unsigned> struct tester;
template<> struct tester<1> {
struct type { };
};
template<> struct tester<2> {
struct type { };
};
template<> struct tester<3> {
struct type { };
};
// etc.
template<class T> class has_template_NAME {
private:
template<class U>
static small_t check(
typename tester<
sizeof(binding<U::template NAME>().value)
>::type*
);
template<class U>
static large_t check(...);
public:
static const bool value = sizeof(check<T>(0)) == sizeof(small_t);
};
template<class T> const bool has_template_NAME<T>::value;
// use X, Y, Z above
int main(int argc, char* argv[]) {
std::cout
<< has_template_NAME<X>::value << '\n'
<< has_template_NAME<Y>::value << '\n'
<< has_template_NAME<Z>::value << &std::endl;
return 0;
}
Unfortunately, this gave me weird errors too. For example,
error: template instantiation resulted in unexpected function type of "small_t
(tester<1U>::type *)" (the meaning of a name may have changed since the template
declaration -- the type of the template is "small_t
(tester<sizeof(<unnamed>)>::type *)")
// etc.
???
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]