From: PGK on
I'd like to define a method which takes twice (say) the number of
template parameters which define the class. The following class
attempts to use explicit recursion for this, but fails. Does anyone
know if this is possible?

template <typename Type, typename ... Types>
struct Foo {
void bar(Type t1, Type t2) { std::cout << t1 << " " << t2 <<
std::endl; }
void bar(Type t1, Type t2, Types ... ts) {
std::cout << t1 << " " << t2 << std::endl;
bar(ts...);
}
};

Many thanks,
Graham

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

From: Daniel Krügler on
On 8 Mrz., 02:33, PGK <graham.k...(a)gmail.com> wrote:
> I'd like to define a method which takes twice (say) the number of
> template parameters which define the class. The following class
> attempts to use explicit recursion for this, but fails. Does anyone
> know if this is possible?
>
> template <typename Type, typename ... Types>
> struct Foo {
> void bar(Type t1, Type t2) { std::cout << t1 << " " << t2 <<
> std::endl; }
> void bar(Type t1, Type t2, Types ... ts) {
> std::cout << t1 << " " << t2 << std::endl;
> bar(ts...);
> }
> };

It should be possible, but not as you tried. The reason of failure
is simple due to the fact that once Foo has been instantiated
you have selected a variadic of given sice. To fix this, just
make bar a function template that is constrained regarding
the number of arguments. Your can also add type-matching
requirements via sfinae combinaed with std::is_same or
std::is_convertible, but I omitted that in the following:

template <typename Type, typename ... Types>
struct Foo {
void bar(Type t1, Type t2) { std::cout << t1 << " " << t2 <<
std::endl; }
template<typename... U,
typename = typename std::enable_if<sizeof...(U) == 2*sizeof...
(Types)>::type>
void bar(Type t1, Type t2, U... ts) {
std::cout << t1 << " " << t2 << std::endl;
bar(ts...);
}
};

Whether this works, depends on your Type/Types
combinations. Then pack expansion inside bar
will try to match the first two members of ts
with Type, so this will only work, if each Ui in U
satisfies the constraint std::is_same<Ui, Type>
or std::is_convertible<Ui, Type>.

You can add this as one sfinae blocker in the
dummy type added to bar's template parameter
list.

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! ]

From: PGK on
On 8 Mar, 09:03, Daniel Kr�gler <daniel.krueg...(a)googlemail.com>
wrote:
> On 8 Mrz., 02:33, PGK <graham.k...(a)gmail.com> wrote:
>
> > I'd like to define a method which takes twice (say) the number of
> > template parameters which define the class. The following class
> > attempts to use explicit recursion for this, but fails. Does anyone
> > know if this is possible?
>
> > template <typename Type, typename ... Types>
> > struct Foo {
> > void bar(Type t1, Type t2) { std::cout << t1 << " " << t2 <<
> > std::endl; }
> > void bar(Type t1, Type t2, Types ... ts) {
> > std::cout << t1 << " " << t2 << std::endl;
> > bar(ts...);
> > }
> > };
>
> It should be possible, but not as you tried. The reason of failure
> is simple due to the fact that once Foo has been instantiated
> you have selected a variadic of given sice. To fix this, just
> make bar a function template that is constrained regarding
> the number of arguments. Your can also add type-matching
> requirements via sfinae combinaed with std::is_same or
> std::is_convertible, but I omitted that in the following:
>
> template <typename Type, typename ... Types>
> struct Foo {
> void bar(Type t1, Type t2) { std::cout << t1 << " " << t2 <<
> std::endl; }
> template<typename... U,
> typename = typename std::enable_if<sizeof...(U) == 2*sizeof...
> (Types)>::type>
> void bar(Type t1, Type t2, U... ts) {
> std::cout << t1 << " " << t2 << std::endl;
> bar(ts...);
> }
>
> };
>
> Whether this works, depends on your Type/Types
> combinations. Then pack expansion inside bar
> will try to match the first two members of ts
> with Type, so this will only work, if each Ui in U
> satisfies the constraint std::is_same<Ui, Type>
> or std::is_convertible<Ui, Type>.
>
> You can add this as one sfinae blocker in the
> dummy type added to bar's template parameter
> list.

{ edits: quoted signature and banner removed. please keep readers in mind when
quoting. -mod }

Many thanks, this helps a lot. I like the look of this approach, but
so far
I've had no luck with the class you posted. It will compile when no
class is
instantiated, but if I declare:
Foo<char,int,double> X;
within a minimal "main", I get the error:
var.cpp:40: error: no type named 'type' in 'struct
std::enable_if<false, void>'

Perhaps it's my compiler (gcc 4.3.2); I have 4.4 at home.

One thing I find surprising is that I had thought that a template
parameter pack must be the last entry in a template parameter list.

All the best,
Graham


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

From: Daniel Krügler on
On 8 Mrz., 20:15, PGK <graham.k...(a)gmail.com> wrote:
> On 8 Mar, 09:03, Daniel Kr�gler <daniel.krueg...(a)googlemail.com>
> > Whether this works, depends on your Type/Types
> > combinations. Then pack expansion inside bar
> > will try to match the first two members of ts
> > with Type, so this will only work, if each Ui in U
> > satisfies the constraint std::is_same<Ui, Type>
> > or std::is_convertible<Ui, Type>.
>
> > You can add this as one sfinae blocker in the
> > dummy type added to bar's template parameter
> > list.

[..]

> Many thanks, this helps a lot. I like the look of this approach, but
> so far
> I've had no luck with the class you posted. It will compile when no
> class is
> instantiated, but if I declare:
> Foo<char,int,double> X;
> within a minimal "main", I get the error:
> var.cpp:40: error: no type named 'type' in 'struct
> std::enable_if<false, void>'
>
> Perhaps it's my compiler (gcc 4.3.2); I have 4.4 at home.

This looks like an error in your compiler.
Btw.: After you provided your example, I
think you need some refactoring. IMO the
implementation you want is this one:

template <typename Type, typename ... Types>
struct Foo {
private:
template<typename V>
void do_bar(V t1, V t2) {
std::cout << t1 << " " << t2 << std::endl;
}

template<typename V, typename... U>
void do_bar(V t1, V t2, U... ts) {
std::cout << t1 << " " << t2 << std::endl;
do_bar(ts...);
}
public:
template<typename... U,
typename = typename std::enable_if<
sizeof...(U) == 2 * sizeof...(Types)>::type>
void bar(Type t1, Type t2, U... ts) {
do_bar(t1, t2, ts...);
}
};

Otherwise the compiler will not find the matching
overload with your mixed types.

> One thing I find surprising is that I had thought that a template
> parameter pack must be the last entry in a template parameter list.

This is intended, because function template
arguments can be deduced, so their is no
need that that the variadic parameters are
at the end of the template parameter list.

The fascinating result of this is that
sinfae of c'tors does no longer require a
dummy function parameter.

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! ]

From: PGK on
Would there be any disadvantage in using static_assert, instead of the
extra sfinae template parameter method you present? For example:

template<typename... U>
void bar(Type t1, Type t2, U... ts) {
static_assert(sizeof...(U) == 2 * sizeof...(Types), "Error");
do_bar<0>(t1, t2, ts...);
}

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