
hi all i post this in the hope someone finds this technique useful to make long story short, here is a snippet: template<typename t> //tag deducing util struct base_tag { typedef decltype(get_tag((t*)42)) type; }; template<typename type, typename tag = typename base_tag<type>::type> struct Base //crtp base class { friend tag get_tag(type*); //helper declaration, never defined }; template<typename type> //no mention of tag void f(const Base<type>&) //tag is deduced automatically {} struct Foo : Base<Foo, void> //derived type {}; int main() { Foo foo; f(foo); } the idea is that if we have an associated type (or types) along with the "real" type in the crtp pattern we can make that associated type deduced automatically and even pretend there is no such tag leaving it e.g. for advanced usage more on this here: http://programmizm.sourceforge.net/blog/2012/because-we-can -- Pavel P.S. if you notice a grammar mistake or weird phrasing in my message please point it out

On 24/08/2012 19:50, pavel wrote:
hi all i post this in the hope someone finds this technique useful
to make long story short, here is a snippet:
template<typename t> //tag deducing util struct base_tag { typedef decltype(get_tag((t*)42)) type; };
template<typename type, typename tag = typename base_tag<type>::type> struct Base //crtp base class { friend tag get_tag(type*); //helper declaration, never defined };
template<typename type> //no mention of tag void f(const Base<type>&) //tag is deduced automatically {}
struct Foo : Base<Foo, void> //derived type {};
int main() { Foo foo; f(foo); }
What's the advantage over template<class T> struct tag_of; template<class Derived> struct Base { typedef typename tag_of<Derived>::type tag; }; struct Foo : Base<Foo> { }; template<> struct tag_of<Foo> { typedef void type; };

Mathias wrote on Saturday, August 25, 2012 at 19:31:53:
What's the advantage over
template<class T> struct tag_of;
template<class Derived> struct Base { typedef typename tag_of<Derived>::type tag; };
struct Foo : Base<Foo> { };
template<> struct tag_of<Foo> { typedef void type; };
thanks for the question the advantage is that you may completely ignore the tag as in template<typename type> void f(const Base<type>&); or you may use it straightforwardly like template<typename type typename tag> void g(const Base<type, tag>&); and the most important -- you may explicitly specify tag for a function like template<typename type> void h1(const Base<type, tag1>&); then a type tagged other than 'tag1' can not be supplied to 'h()', for example struct Foo : Base<Foo, tag1> {} struct Bar : Base<Bar, tag2> {} int main() { Foo foo; f(foo); g(foo); h1(foo); Bar bar; f(bar); g(bar); h1(bar); //does not compile by design: tag mismatch } declaration is crystal clean (read: self documenting), no need to use enable_if, etc. as a bonus compilers -- at least msvc and gcc -- produce nice error messages like "there is no such function 'f' taking 'type'" though gcc is much nicer in this regard giving complete, exhaustive description of the error -- Pavel P.S. if you notice a grammar mistake or weird phrasing in my message please point it out

On 26/08/2012 14:28, pavel wrote:
Mathias wrote on Saturday, August 25, 2012 at 19:31:53:
What's the advantage over
template<class T> struct tag_of;
template<class Derived> struct Base { typedef typename tag_of<Derived>::type tag; };
struct Foo : Base<Foo> { };
template<> struct tag_of<Foo> { typedef void type; };
thanks for the question
the advantage is that you may completely ignore the tag as in
template<typename type> void f(const Base<type>&);
This is also possible with the code above, since the tag is not part of the template parameters of Base.

Mathias wrote on Tuesday, August 28, 2012 at 16:21:01:
On 26/08/2012 14:28, pavel wrote:
thanks for the question
the advantage is that you may completely ignore the tag as in
template<typename type> void f(const Base<type>&);
This is also possible with the code above, since the tag is not part of the template parameters of Base.
the quote is out of context (or was that your intention?) the full quote should be:
the advantage is that you may completely ignore the tag as in
template<typename type> void f(const Base<type>&);
or you may use it straightforwardly like
template<typename type typename tag> void g(const Base<type, tag>&);
and the most important -- you may explicitly specify tag for a function like
template<typename type> void h1(const Base<type, tag1>&);
notice the "or" and "and" clauses -- Pavel P.S. if you notice a grammar mistake or weird phrasing in my message please point it out

On 24.08.2012 19:50, pavel wrote:
hi all i post this in the hope someone finds this technique useful
to make long story short, here is a snippet:
template<typename t> //tag deducing util struct base_tag { typedef decltype(get_tag((t*)42)) type; };
template<typename type, typename tag = typename base_tag<type>::type> struct Base //crtp base class { friend tag get_tag(type*); //helper declaration, never defined };
template<typename type> //no mention of tag void f(const Base<type>&) //tag is deduced automatically {}
I think this only happens to work by accident, because compilers tend to defer instantiation of function templates until the end of the translation unit, when a proper instantiation of Base is available. But without a compiler that doesn't I don't think I can come up with code that shows this. In any case, this makes *my* head hurt, and that's saying something. Sebastian

Sebastian wrote on Monday, August 27, 2012 at 12:28:16:
On 24.08.2012 19:50, pavel wrote: I think this only happens to work by accident, because compilers tend to defer instantiation of function templates until the end of the translation unit, when a proper instantiation of Base is available. But without a compiler that doesn't I don't think I can come up with code that shows this.
the thing is in the order of instantiation when you define a concrete type, e.g. struct Foo : Base<Foo, tag1> {}; the 'Base' template is instantiated and the friend function declaration, namely tag1 get_tag(Foo*); comes into play at this point further when you supply an object of 'Foo' to a template function taking a ref to 'Base' the second template argument (here -- tag) is deduced according to the declaration of 'Base': template<typename the_type, typename tag = typename base_tag<type>::type> struct Base { friend tag get_tag(the_type*); } the compiler evaluates the default type instantiating 'base_tag' (remember that the appropriate 'Base' is already instantiated and needed 'get_tag()' is in the game either) so the 'base_tag' does nothing more than just wrapping the decltype expression in a typedef: typedef decltype(get_tag((the_type*)314159)) type; at this point the compiler have the complete set of pieces of the puzzle: - 'get_tag()' declaration from the 'Base' template instatioation earlier - 'the_type' deduced from the function argument as in template<typename the_type> void f(const Base<the_type>&); so the compiler gets the exact type associated with 'the_type' ('Foo' in this example), namely -- 'tag1' according to 'Foo' definition: struct Foo : Base<Foo, tag1> {}; and we have now the deduced type for the argument of a function, namely 'Base<Foo, tag1>', which is exactly the base class of the 'Foo' so any (modern) standard conforming compiler should do this (feel free to prove me wrong) sorry for lengthy explanation
In any case, this makes *my* head hurt, and that's saying something.
just use it if you'd like to -- Pavel P.S. if you notice a grammar mistake or weird phrasing in my message please point it out

On 27.08.2012 12:49, pavel wrote:
On 24.08.2012 19:50, pavel wrote: I think this only happens to work by accident, because compilers tend to defer instantiation of function templates until the end of the translation unit, when a proper instantiation of Base is available. But without a compiler that doesn't I don't think I can come up with code that shows this.
Sebastian wrote on Monday, August 27, 2012 at 12:28:16: the thing is in the order of instantiation
so any (modern) standard conforming compiler should do this (feel free to prove me wrong)
You're right, I forgot that f cannot possibly be instantiated before Base. Sebastian
participants (3)
-
Mathias Gaunard
-
pavel
-
Sebastian Redl