Topic: Variadic templates
Author: tskorohod@voliacable.com
Date: Mon, 9 Oct 2006 15:24:57 CST Raw View
> You can't do that with variadic templates because the only
> way to represent two lists is as a list of pairs in the
> first place .. :)
That's easy:
template<typename... Args1>
struct zip
{
template<typename... Args2>
struct with
{
typedef std::tr1::tuple< std::pair<Args1,Args2>... > type;
};
};
int main()
{
zip<int,float,double>::with<float,int,double>::type tuple(
std::make_pair(1,2.f),
std::make_pair(2.f,0),
std::make_pair(1.,2.)
);
return 0;
}
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: hgardner@rawbw.com (Howard Gardner)
Date: Thu, 7 Sep 2006 20:34:26 GMT Raw View
Greg Herlihy wrote:
> Howard Gardner wrote:
>
>> These are the particular stumbling blocks that hurt the worst, and I run
>> into them fairly often (or, more correctly, I deploy workarounds for
>> them fairly often).
>>
>> 1) If you write:
>>
>> template< typename T, T V > struct sometype;
>>
>> then there's no way to partially specialize it for a particular type. It
>> seems to me that this should work (but of course it doesn't).
>>
>> template< int V > struct sometype< int, V >;
>
> .because the partial specialization of "sometype" does not declare a
> complete type. A full or partial specialization of a class template
> needs to be a complete type in order to instantiate objects with it:
>
> template< int V >
> struct sometype< int, V > {};
>
> Now the partial specialization of sometype works. For example:
>
> sometype< int, 7> mytype;
>
> will use the partial specialization above.
#include <ostream>
#include <cstddef>
using namespace std;
template< typename T, T V >
struct sometype{static const int value = 0;};
template< int V >
struct sometype< int, V > {static const int value = 1;};
int main()
{
cout << sometype< int, 4 >::value << endl;
}
Using comeau, this prints "0". It instantiated the primary template, not
the specialization.
There are ways to work around this, for example:
#include <ostream>
#include <cstddef>
using namespace std;
template< typename T, T V >
struct meta_k{static const T value = V;};
template< int V >
struct int_meta_k : public meta_k< int, V >{};
template< typename T >
struct sometype;
template< int V >
struct sometype< int_meta_k< V > > {static const int value = 1;};
int main()
{
cout << sometype< int_meta_k< 4 > >::value << endl;
}
but that means:
1. "whatever_meta_k" for every nontype that I want to specialize on
2. "sometype" specialization to match the "whatever_meta_k"
3. user using the proper "whatever_meta_k"
1+2 make work for the library implementer, and for the user if he tries
to extend the library to deal with a new nontype.
3 complicates the library interface.
3 also places a defacto constraint on the design of sometype. If the
primary template is not left incomplete, then it becomes very easy for
the user to write a program that compiles but doesn't work right.
This is another workaround:
#include <ostream>
#include <cstddef>
using namespace std;
template< typename T >
struct meta_k
{
template< T V >
struct invoke{static const T value = V;};
};
template< typename T >
struct sometype;
template< int V >
struct sometype< meta_k< int >::invoke< V > >
{static const int value = 1;};
int main()
{
cout << sometype< meta_k< int >::invoke< 4 > >::value << endl;
}
Now only the per-nontype specialization of sometype is needed. That
saves a little work for the library implementer and library users who
extend it.
It still complicates the interface to the library in a different way,
which is arguably worse than the original complication.
The defacto constraint on the design of sometype is still there.
There are probably other workarounds, but I've yet to find one that
doesn't involve complicating the library interface and constraining the
design of sometype.
Both of those workarounds create genuine problems for the *user* of the
library.
>
>> 2) There is no way to specify a generic nontype template parameter. This
>> would be nice:
>>
>> template< nontype > struct something;
>> template< > struct something< char * >;
>> template< typename T > something< T * >;
>> template< typename T > something< T ** >;
>>
>> so I can write a template that will take an integer, a particular type
>> of pointer, a reference, a pointer to a pointer to anything, etc.
>
> Why would a nontype parameter be useful here? A "something" template
> with one parameterized type, such as:
>
> template< class T > struct something {};
>
> can be specialized in all the ways shown above:
>
> template< > struct something< char * > {};
> template< class T > struct something< T * > {};
> template< class T > struct something< T ** > {};
Yeah, botched. I'll try again :o
It seems that it is impossible to properly implement is_template in the
current language, and it doesn't seem that anyone is proposing to make
it possible. (If I'm wrong on either of those scores, I would embrace
enlightenment.)
The facility is useful in its own right, but changing the language so
that it can be implemented is even more useful. The problems that
prevent a good implementation of is_template prevent the good
implementations of many other facilities too.
Here is a partial implementation of "is_template". It's the best that I
have been able to devise.
#include <ostream>
#include <cstddef>
using namespace std;
template< typename >
struct is_template
{static const bool value = false;};
template< template< typename > class X, typename T >
struct is_template< X< T > >
{static const bool value = true;};
template< template< int > class X, int V >
struct is_template< X< V > >
{static const bool value = true;};
template
<
template< template< typename > class > class X0,
template< typename > class X1
>
struct is_template< X0< X1 > >
{static const bool value = true;};
template< typename >
struct type_template;
template< int >
struct int_template;
template< template< typename > class >
struct type_template_template;
template< bool >
struct uncovered_template;
int main()
{
cout << is_template< type_template< int > >::value << endl;
cout << is_template< int_template< 20 > >::value << endl;
cout
<< is_template< type_template_template< type_template > >::value
<< endl;
cout << is_template< uncovered_template< false > >::value << endl;
}
This prints 1 1 1 0.
The 0 at the end is a real tragedy: the program has compiled, but it
doesn't work. That happened because there are two conflicting
constraints on is_template:
1. The logic of the design demands that the primary template is complete.
2. The structure of the implementation demands that the primary template
is not complete.
Unless there is a radically different approach available (a different
design or a different implementation), then it seems that the choice is
either to abandon the facility altogether or accept the consequences of
an implementation that compiles but lies.
This would be a much better compromise implementation:
template< typename >
struct is_template
{static const bool value = false;};
template< template< typename > class X, typename T >
struct is_template< X< T > >
{static const bool value = true;};
template< template< nontype > class X, nontype V >
struct is_template< X< V > >
{static const bool value = true;};
template
<
template< template< typename... > class > class X,
typename... T
>
struct is_template< X< T > >
{static const bool value = true;};
template
<
template< template< nontype... > class > class X,
nontype... V
>
struct is_template< X< V > >
{static const bool value = true;};
I haven't detected any nontype proposal, and I'm not at all sure that
the variadic template proposal is supposed to allow this.
Even if it works as imagined, the implementation would still be flawed
in two ways: there are an infinite number of permutations of the
template template versions, and only templates with a template parameter
arity of one are detected. It would be feasible to create a practical
implementation of is_template that relied on PP macros to cover a
reasonable set of template template parameters and a generous number of
template parameter arities.
This could cover help cover the issue with template template parameters:
template< typename >
struct is_template
{static const bool value = false;};
template< template< auto > class X, auto A >
struct is_template< X< A > >
{static const bool value = true;};
How thorough that is depends on what auto can match. To be perfect, it
needs to match "any sequence of types, nontypes, and templates."
The variadic template proposal has the same issue:
typename... (any number of types)
nontype... (any number of nontypes)
template< typename > class... (any number of templates accepting one type)
template< typename... > class... (any number of templates accepting any
number of types)
if auto means "typename or nontype", then:
template< auto >... (any number of templates accepting a type or a nontype)
template< auto... > class... (any number of templates accepting any
combination of types or nontypes)
If auto means "typename or nontype or template accepting anything at
all", then you can implement is_template perfectly. If you really meant
"any combination of types, nontypes, and templates accepting one type"
then you'd have to resort to where clauses (ie, you'd have to write a
where clause to pick only the templates that you meant to allow).
If that can be worked out, it's probably reasonable to just hand write
one case for each template parameter arity to be supported.
This could make that unnecessary:
template< typename >
struct is_template< typename >
{static const bool value = false;};
template< template< auto... > class X, auto A... >
struct is_template< X< A > >
{static const bool value = true;};
This is a big set of changes, working out a proper proposal would be a
lot of work, and implementing them would be even more work.
There are three proposals that address major issues in generic
programming: typeof (was this accepted?), concepts, and variadic
templates. Are there any that address the things that "nontype"
addresses, or that "auto" addresses?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 8 Sep 2006 00:05:27 CST Raw View
Howard Gardner wrote:
> Greg Herlihy wrote:
> > Howard Gardner wrote:
> >
> >> These are the particular stumbling blocks that hurt the worst, and I run
> >> into them fairly often (or, more correctly, I deploy workarounds for
> >> them fairly often).
> >>
> >> 1) If you write:
> >>
> >> template< typename T, T V > struct sometype;
> >>
> >> then there's no way to partially specialize it for a particular type. It
> >> seems to me that this should work (but of course it doesn't).
> >>
> >> template< int V > struct sometype< int, V >;
> >
> > .because the partial specialization of "sometype" does not declare a
> > complete type. A full or partial specialization of a class template
> > needs to be a complete type in order to instantiate objects with it:
> >
> > template< int V >
> > struct sometype< int, V > {};
> >
> > Now the partial specialization of sometype works. For example:
> >
> > sometype< int, 7> mytype;
> >
> > will use the partial specialization above.
>
> #include <ostream>
> #include <cstddef>
> using namespace std;
>
> template< typename T, T V >
> struct sometype{static const int value = 0;};
>
> template< int V >
> struct sometype< int, V > {static const int value = 1;};
>
> int main()
> {
> cout << sometype< int, 4 >::value << endl;
> }
>
> Using comeau, this prints "0". It instantiated the primary template, not
> the specialization.
.which is wrong. A matching specialization is always considered a
better match than the general class template. And given the fact that
there are no other specializations of "sometype" from which to choose,
there can be no doubt that the value of sometype<int, 4>::value has to
be "1".
I would suggest reporting this bug to the compiler maker. You may also
wish to consider switching to another C++ compiler that does not have
this defect. I can confirm, for example, that gcc 4.01 selects the
sometype specialization correctly.
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: hgardner@rawbw.com (Howard Gardner)
Date: Fri, 8 Sep 2006 16:16:03 GMT Raw View
Greg Herlihy wrote:
>> Using comeau, this prints "0". It instantiated the primary template, not
>> the specialization.
>
> .which is wrong. A matching specialization is always considered a
> better match than the general class template. And given the fact that
> there are no other specializations of "sometype" from which to choose,
> there can be no doubt that the value of sometype<int, 4>::value has to
> be "1".
You're right, it should have worked.
> I would suggest reporting this bug to the compiler maker.
It's fixed in 4.3.8 ALPHA.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: hossein.haeri@gmail.com
Date: Mon, 4 Sep 2006 17:16:58 CST Raw View
Hi Doug,
Well, a very important advantage this may bring to us, and which AFAICS
you haven't mentioned, is that it removes the bothers resulting when --
as extensions -- implementations add template parameters to the
standard ones. In case I'm not clear this is what I mean:
Standard says:
template <class T, class Allocator = allocator<T> >
class vector;
And, there well may be -- if not already existing -- compilers which
add parameters like:
template <class T, class Allocator = allocator<T>, class myParameter>
class vector;
As Nico (Jossuttis) and and Vandervoorde also mention in C++ Templates,
this is fully standard-conforming. But, unfortunately, as they also
mention again, this may cause protability problems. Variadic Templates
will remove this. Or, I hope so at least.
Despite that, I have my own worries if this won't overcomplicate C++
which even right now needs simplification. Or, his majesty, will dye
out... :(
(I'm in a hurry, so I can't revise... :( )
TTFN,
--Hossein
Douglas Gregor wrote:
> Variadic templates are templates that can take an arbitrary number of
> "extra" template arguments. Most templates take a fixed number of
> parameters. For example, an STL map has 4 parameters (Key, Data,
> Compare, Alloc) while an STL pair has two parameters (T and U). But
> what happens when you want to generalize a pair into a tuple (for any
> N), like in the Boost Tuples library or Library TR1 "tuple" facility?
> You really need tuple to take any number of template arguments, so that
> one can use tuple<int, float>, tuple<char, short, int, long>, or even
> tuple<>: variadic templates allow you to do so, by creating class and
> function templates that accept any number of arguments. Variadic
> templates also allow you to perform transformations on all of these
> arguments in one step, turning many non-trivial metaprograms into
> simple one-line statements and eliminating a great deal of code
> repetition.
>
> Variadic templates are a relatively small but very powerful extension
> to C++. With variadic templates, we can implement a completely
> type-safe printf() facility that, unlike the C printf(), works equally
> well for user-defined and built-in types. We have also been able to
> implement several facilities in Library TR1 and Boost without resorting
> to preprocessor metaprogramming, external generators, or manual code
> repetition. In particular, we have implemented the tuple, function, and
> bind libraries, and using variadic templates their implementations are
> significantly shorter than without.
>
> We intend to bring variadic templates to the C++ committee meeting in
> Portland and propose their inclusion in C++0x. Before that, however, we
> would like some feedback from the C++ community. Additional information
> about variadic templates, including a complete implementation in GCC,
> is available here:
>
>
> http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html
>
> A brief introduction to variadic templates:
> http://www.generic-programming.org/~dgregor/cpp/brief-intro.pdf
>
> The variadic templates proposal:
>
> http://www.generic-programming.org/~dgregor/cpp/variadic-templates.pdf
>
>
> Cheers,
> Doug Gregor
> doug.gregor@gmail.com
>
> ---
> [ 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.comeaucomputing.com/csc/faq.html ]
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: brok@spam-trap-cop.net ("Bronek Kozicki")
Date: Tue, 5 Sep 2006 14:51:07 GMT Raw View
hossein.haeri@gmail.com wrote:
> Despite that, I have my own worries if this won't overcomplicate C++
> which even right now needs simplification. Or, his majesty, will dye
> out... :(
I'm not worried about that. If this facility makes MY programs simpler,
safer and easier to maintain (and it definitely seems it will), I want
it. The same applies to other features planned for C++0x, eg. concepts.
It is not that much important how complex language is (C++ crossed the
line in its first ISO standard edition anyway), but how difficult to
write and maintain programs written in this language are. If some new
feature makes it easy to write better programs and plays nice with
existing language features, then it is definitely worth considering.
B.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: brok@spam-trap-cop.net ("Bronek Kozicki")
Date: Tue, 5 Sep 2006 18:36:55 GMT Raw View
"Bronek Kozicki" <brok@spam-trap-cop.net> wrote:
> It is not that much important how complex language is (C++ crossed the
> line in its first ISO standard edition anyway), but how difficult to
^^^^^^^^^
I meant "easy" here.
Just to make myself clear: I consider variadic templates far superior
to Boost.MPL, Loki or my own typelists solutions, all based on
metaprogramming and macros hackery. This hackery is a visible proof
that variadic templates proposal *is* addressing real problems in C++ .
B.
--
Remove -trap- when replying. Usun -trap- gdy odpisujesz.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: SeeWebsiteForEmail@erdani.org ("Andrei Alexandrescu (See Website For Email)")
Date: Tue, 5 Sep 2006 23:33:54 GMT Raw View
Bronek Kozicki wrote:
> "Bronek Kozicki" <brok@spam-trap-cop.net> wrote:
>
>> It is not that much important how complex language is (C++ crossed the
>> line in its first ISO standard edition anyway), but how difficult to
>
> ^^^^^^^^^
> I meant "easy" here.
>
> Just to make myself clear: I consider variadic templates far superior
> to Boost.MPL, Loki or my own typelists solutions, all based on
> metaprogramming and macros hackery. This hackery is a visible proof
> that variadic templates proposal *is* addressing real problems in C++ .
Definitely. By the way, the authors might also add Modern C++ Design and
Loki to the pile of justificative previous work. Most of the gratuitous
complications MC++D and Loki go through to implement and use typelists
would be radically reduced by the proposed facility.
Andrei
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Tue, 5 Sep 2006 22:23:14 CST Raw View
> ColoredPoint might inherit from it adding a variable Color.
I would just like to make one small point, considering especially that
Andrei Alexandrescu is a stickler for precision in matters of logic. A
point is by definition, infinitessimally small and therefore cannot
have a colour. There is no surface there to be coloured.
regards
Andy Little
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: SeeWebsiteForEmail@erdani.org ("Andrei Alexandrescu (See Website For Email)")
Date: Wed, 6 Sep 2006 03:49:50 GMT Raw View
kwikius wrote:
>>ColoredPoint might inherit from it adding a variable Color.
>
>
> I would just like to make one small point, considering especially that
> Andrei Alexandrescu is a stickler for precision in matters of logic. A
> point is by definition, infinitessimally small and therefore cannot
> have a colour. There is no surface there to be coloured.
>
> regards
> Andy Little
Thanks, Andy.Little corrections are always welcome, as are innocuous
puns I hope :o). Let's replace Point with Pixel throughout, and let's
replace color with colour, too. We could even use the preprocessor to
help with that :oD.
Andrei
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: alec@arlross.demon.co.uk (Alec Ross)
Date: Wed, 6 Sep 2006 14:49:04 GMT Raw View
In message <1157502178.979502.168860@d34g2000cwd.googlegroups.com>,
kwikius <andy@servocomm.freeserve.co.uk> writes
>
>> ColoredPoint might inherit from it adding a variable Color.
>
>I would just like to make one small point, considering especially that
>Andrei Alexandrescu is a stickler for precision in matters of logic. A
>point is by definition, infinitessimally small and therefore cannot
>have a colour. There is no surface there to be coloured.
>
Nope, but the concept still makes sense. That is, a cp is an instance
of a type that supports at least some of a "point-like", interface, and
also has an additional interface for something called colour/color.
(Cf the use of colour as an attribute in the physics sub-atomic
particles.)
In the context of typical OO-101 examples, and computer graphics
implementations, there is a history of the use of cp s, which I take be
behind Andrei's illustration.
Often in the OO examples people derive Cp from a Point class - though I
tend prefer aggregation. (With "actual", or rendering size, or even
shape yet another attribute(s).) To my taste this keeps the Point-ness
a separate mathematical notion, as you seem to like as well.
Sure, in many cases such Cp types would better be called "Pixel"s, or
whatever. But, my point (;)) is that the notion of a cp, as a point w/
colour can make conceptual/analytical sense as well.
Best,
Alec
--
Alec Ross
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Wed, 6 Sep 2006 11:27:19 CST Raw View
Andrei Alexandrescu (See Website For Email) wrote:
> Douglas Gregor wrote:
> > Variadic templates are templates that can take an arbitrary number of
> > "extra" template arguments.
> [...]
> > We intend to bring variadic templates to the C++ committee meeting in
> > Portland and propose their inclusion in C++0x. Before that, however, we
> > would like some feedback from the C++ community.
>
> Very nice proposal! In line with the authors, I believe that variadic
> templates are an important missing feature in C++; I personally view the
> recent recrudescence of preprocessor usage the most worrisome and
> regressive trends in today's C++.
>
> I have just a few nits about the proposal, dealing with mostly things
> like term definitions, introduced syntax, and the such. My comments
> mostly refer to the paper at
> http://www.generic-programming.org/~dgregor/cpp/variadic-templates.pdf,
> although I've also read the shorter introductory materials (to which I
> also think the nits apply).
Wonderful. Thank you for the detailed comments; where I don't respond
to something below, assume I'm nodding my head in agreement and
resolving to improve things in the next draft.
> typedef U Args;
>
> My interpretation of the paper is that it's illegal to do that, but the
> paper doesn't make that clear; in fact, the quoted paragraph---due to
> the ambiguous terminology it uses---suggest that I could.
Yep, your interpretation is correct, and the paragraph quoted is very
ambiguous.
> * My unhappiness with the chosen wording can be summarized in the
> following quote:
>
> "Here we are using the ellipsis operator to pack or unpack a template
> parameter pack."
>
> So let's see. We have a pack, why in the world do we have to pack it
> again? (To say nothing about using "pack" as both a noun and a verb
> closely and repeatedly throughout the paper.)
>
> My feeling is that this comes from not being firm enough in stating that
> template parameter packs are a distinct entity, and clearly defining the
> operations that come along with it. If that happened, maybe some
> redundant unpacking and "uberpacking" (meaning, to pack a pack that was
> already packed) could have been avoided. For example:
>
> template<typename T, typename... Args>
> struct count<T, Args...> {
> static const int value = 1 + count<Args...>::value;
> };
>
> In here, we know Args is NOT a type, but it's a template parameter pack
> (TPP). Then, without risk of ambiguity, can't we drop the "...", can't
> we? There's no other possible use of Args within a template:
>
> template<typename T, typename... Args>
> struct count<T, Args...> {
> static const int value = 1 + count<Args>::value;
> };
>
> If there were reasons that made this undesirable, the paper doesn't
> clarify them, thus leaving minimalists unhappy.
This certainly needs clarification. The ellipsis is necessary because
we need to know where to expand the parameter pack. We don't always
want to expand it immediately, as in the count example. Let's look at a
metafunction "one_tuples" that creates a tuple of 1-tuples, e.g.,
one_tuples<int, float>::type would be tuple<tuple<int>, tuple<float> >.
We can write this metafunction as:
template<typename... Args>
struct one_tuples {
typedef tuple<tuple<Args>...> type;
};
This works with the current spec, but if we added the immediate
expansion of "Args" (like we needed for the shortened "count"), it
would result in the equivalent of:
typedef tuple<tuple<Args...>...> type;
The second ellipsis would then be an error. There is a more complicated
rule that would make both the shortened count and one_tuples work, but
implementing it involves a great deal of backtracking in the compiler.
That wouldn't fit into the one-week development time :)
> * About the tuple example in 2.2: the example has tuple<char, int,
> double> privately inherit tuple<int, double> and so on. Nice. Yet the
> interesting case is one of structural subtyping, in which tuple<char,
> int, double> inherits (publically this time) tuple<char, int> which
> inherits tuple<char>. Then I can pass a "larger" tuple when a "smaller"
> tuple is expected, which is sound. But it's unclear on how to do that,
> and an example on how to do it would be most instructive. I have an idea
> on how to do it by first defining a Reverse<...> helper, but maybe more
> elegant means are possible and desirable. For example, matching
> something at the end of a parameter pack?
I can't think of anything more elegant than using Reverse. As you note,
matching at the end of a parameter pack would work, e.g.,
template<typename... Head, typename Last>
struct tuple<Head..., Last> : public tuple<Head...> {
// rest of the implementation
};
When we looked at this before, we decided that the cost of this feature
was not worth its gain. Allowing deduction anywhere but at the end of
the parameter list opens up a can of worms that we weren't sure how to
close. Consider, for instance:
template<typename... Args1, typename... Args2>
void foo(Args1... args1, int middle, Args2... args2);
Eeep!
> * "Variadic templates build upon the ideas of C's variable-length
> function parameter lists." I fail to see any relationship, and
> mentioning one based on whatever slight resemblance does nothing but
> diminish the merit of the paper by associating it with what's widely
> considered an ill-designed feature.
Hah. Good point. I might as well say that the implementation of a new
feature is "slightly less complicated than export" :)
> * In the printf example, again it's unclear why printf(++s, args...) is
> necessary when printf(++s, args) would be just as unambiguous and less
> baroque.
Right; the ellipsis is mandatory here for the same reason as above.
Thanks again for the detailed comments!
Cheers,
Doug
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail@erdani.org>
Date: Wed, 6 Sep 2006 13:57:28 CST Raw View
Douglas Gregor wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>>In here, we know Args is NOT a type, but it's a template parameter pack
>>(TPP). Then, without risk of ambiguity, can't we drop the "...", can't
>>we? There's no other possible use of Args within a template:
>>
>>template<typename T, typename... Args>
>>struct count<T, Args...> {
>> static const int value = 1 + count<Args>::value;
>>};
>>
>>If there were reasons that made this undesirable, the paper doesn't
>>clarify them, thus leaving minimalists unhappy.
>
>
> This certainly needs clarification. The ellipsis is necessary because
> we need to know where to expand the parameter pack. We don't always
> want to expand it immediately, as in the count example. Let's look at a
> metafunction "one_tuples" that creates a tuple of 1-tuples, e.g.,
> one_tuples<int, float>::type would be tuple<tuple<int>, tuple<float> >.
[...]
> This works with the current spec, but if we added the immediate
> expansion of "Args" (like we needed for the shortened "count"), it
> would result in the equivalent of:
>
> typedef tuple<tuple<Args...>...> type;
>
> The second ellipsis would then be an error. There is a more complicated
> rule that would make both the shortened count and one_tuples work, but
> implementing it involves a great deal of backtracking in the compiler.
> That wouldn't fit into the one-week development time :)
I now understand. I think it would help greatly if you specified that
(and please let me know if I'm misunderstanding things): "..." to the
right of an expression or type specification involving a type or value
pack P of N elements is a metaoperator that expands that expression or
type specification into the corresponding N expressions or type
specifications, conceptually comma-separated, EXCEPT for instances of
the old meaning of "...", which sucks and stays with unchanged
semantics. ("Which sucks" shouldn't be mentioned in the proposal.)
Also, "..." to the right of (1) "typename", (2) "class", (3) an actual
type, is syntax that introduces a parameter pack. So there are really
two new uses of "...": metaoperator and syntax. (I say "metaoperator"
because it's an operator doing its job during compilation, unlike usual
operators.)
I'm not sure if my rule is correct, but it seems to cover your examples
very nicely:
- When we see printf(s, args...); I understand that "..." applies to
"args" and expands it to the comma-separated values packed inside args.
- When we see template<typename T, typename... Args> struct count<T,
Args...> {}; we known to expand the second "..." into whatever arguments
come down the pike for count.
- When we see template <typename... Args> void printf(const char *s,
const Args&...) we know that the typespec 'const Args&' will be nicely
expanded into the comma-separated typespecs.
And so on. If I just saw this rule someplace after the first simple
example, I could have navigated the more complex examples with ease. Now
I can even come up with more intricate examples that I'm not sure how
would work:
printf(s, args ? args + 5 : args * 6...);
Would this expand each arg in the pack to the expression? By the way,
the precedence of "..." should be specified - I guess the obvious choice
is to make it the bottom of the operator hierarchy, so it catches the
longest expressions.
>>Then I can pass a "larger" tuple when a "smaller"
>>tuple is expected, which is sound. But it's unclear on how to do that,
>>and an example on how to do it would be most instructive. I have an idea
>>on how to do it by first defining a Reverse<...> helper, but maybe more
>>elegant means are possible and desirable. For example, matching
>>something at the end of a parameter pack?
>
>
> I can't think of anything more elegant than using Reverse. As you note,
> matching at the end of a parameter pack would work, e.g.,
>
> template<typename... Head, typename Last>
> struct tuple<Head..., Last> : public tuple<Head...> {
> // rest of the implementation
> };
>
> When we looked at this before, we decided that the cost of this feature
> was not worth its gain. Allowing deduction anywhere but at the end of
> the parameter list opens up a can of worms that we weren't sure how to
> close. Consider, for instance:
>
> template<typename... Args1, typename... Args2>
> void foo(Args1... args1, int middle, Args2... args2);
>
> Eeep!
I understand. Looks a lot like those examples "a context-free grammar
could never match this pattern" :o).
I will duly note, however, that Reverse<...> takes linear time and
space. This is the downside of limiting the feature to right-branching only.
Andrei
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Wed, 6 Sep 2006 16:06:06 CST Raw View
Andrei Alexandrescu (See Website For Email) wrote:
> I now understand. I think it would help greatly if you specified that
> (and please let me know if I'm misunderstanding things): "..." to the
> right of an expression or type specification involving a type or value
> pack P of N elements is a metaoperator that expands that expression or
> type specification into the corresponding N expressions or type
> specifications, conceptually comma-separated, EXCEPT for instances of
> the old meaning of "...", which sucks and stays with unchanged
> semantics. ("Which sucks" shouldn't be mentioned in the proposal.)
>
> Also, "..." to the right of (1) "typename", (2) "class", (3) an actual
> type, is syntax that introduces a parameter pack. So there are really
> two new uses of "...": metaoperator and syntax. (I say "metaoperator"
> because it's an operator doing its job during compilation, unlike usual
> operators.)
Yep, this is a good rule. I had a short discussion offline with someone
you referred to the ellipsis "on the left" (meaning, left of the name
of something being declared, e.g., a template parameter pack or
function parameter pack) vs. "on the right" (meaning, right of the
expression or type). An ellipsis on the left declares a parameter pack,
an ellipsis on the right is a pack or unpack.
> I'm not sure if my rule is correct, but it seems to cover your examples
> very nicely:
Yep, your rule works well. Thanks!
> And so on. If I just saw this rule someplace after the first simple
> example, I could have navigated the more complex examples with ease. Now
> I can even come up with more intricate examples that I'm not sure how
> would work:
>
> printf(s, args ? args + 5 : args * 6...);
>
> Would this expand each arg in the pack to the expression? By the way,
> the precedence of "..." should be specified - I guess the obvious choice
> is to make it the bottom of the operator hierarchy, so it catches the
> longest expressions.
"..." has lower precedence than anything except the comma that
separates arguments in a function call, function type, or template
argument list. So your printf call above would expand to the following,
if we think of args as containing two arguments, args1 and args2:
printf(s, args1 ? args1 + 5 : args1 * 6, args2 ? args2 + 5 : args2 *
6);
> > close. Consider, for instance:
> >
> > template<typename... Args1, typename... Args2>
> > void foo(Args1... args1, int middle, Args2... args2);
> >
> > Eeep!
>
> I understand. Looks a lot like those examples "a context-free grammar
> could never match this pattern" :o).
:)
> I will duly note, however, that Reverse<...> takes linear time and
> space. This is the downside of limiting the feature to right-branching only.
You are absolutely correct.
Doug
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Thu, 7 Sep 2006 00:41:25 CST Raw View
Howard Gardner wrote:
>
> These are the particular stumbling blocks that hurt the worst, and I run
> into them fairly often (or, more correctly, I deploy workarounds for
> them fairly often).
>
> 1) If you write:
>
> template< typename T, T V > struct sometype;
>
> then there's no way to partially specialize it for a particular type. It
> seems to me that this should work (but of course it doesn't).
>
> template< int V > struct sometype< int, V >;
.because the partial specialization of "sometype" does not declare a
complete type. A full or partial specialization of a class template
needs to be a complete type in order to instantiate objects with it:
template< int V >
struct sometype< int, V > {};
Now the partial specialization of sometype works. For example:
sometype< int, 7> mytype;
will use the partial specialization above.
> 2) There is no way to specify a generic nontype template parameter. This
> would be nice:
>
> template< nontype > struct something;
> template< > struct something< char * >;
> template< typename T > something< T * >;
> template< typename T > something< T ** >;
>
> so I can write a template that will take an integer, a particular type
> of pointer, a reference, a pointer to a pointer to anything, etc.
Why would a nontype parameter be useful here? A "something" template
with one parameterized type, such as:
template< class T > struct something {};
can be specialized in all the ways shown above:
template< > struct something< char * > {};
template< class T > struct something< T * > {};
template< class T > struct something< T ** > {};
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]