template<typename type, typename tag> struct Base {}; template<typename type, typename tag> void f(const Base<type, tag>&) {} struct Foo : Base<Foo, void> {}; int main() { Foo foo; f(foo); }
f()
really don't care what tag is in its argument, and it accepts an object of any type inherited from Base
. But wouldn't it be more convenient if we just omit the tag that is not interesting to us? Check out: template<typename t> struct base_tag { typedef decltype(get_tag((t*)42)) type; }; template<typename type, typename tag = typename base_tag<type>::type> struct Base { friend tag get_tag(type*); //never defined }; template<typename type> void f(const Base<type>&) {} struct Foo : Base<Foo, void> {}; int main() { Foo foo; f(foo); }
f()
, we intuitively understand that functions really do not care what the tag of its argument.Base
class, a friend function is declared, which returns a tag and accepts a pointer to an inherited type. Note that this function does not need to be defined. When we define a type, for example, Foo
, we actually declare the function with the corresponding prototype, in this case: void get_tag(Foo*);
f()
, while creating a template instance (template instantiation), the compiler tries to determine the default template argument for the function argument (which is an object of the Foo
class):base_tag
template base_tag
compiler gets the tag type,get_tag()
function, with pointer Foo*
as an argument,Base
class with the type Foo
and void
as template arguments, that is, Base<Foo, void>
template<typename type> void g(const Base<type, int>&) {} struct Bar : Base<Bar, int> {}; int main() { Foo foo; Bar bar; f(foo); f(bar); g(foo); //doesn't compile by design g(bar); }
g(foo)
intentionally does not allow compiling the code, because the argument tag must be of type int
(while it is void
for Foo
). In such a situation, the compiler gives a beautiful error message. Well, at least MSVC10 and GCC4.7.main.cpp (30): error C2784: 'void g (const Base <type, int> &)': could not deduce template argument for 'const Base <type, int> &' from 'Foo' main.cpp (18): see declaration of 'g'
source.cpp: In function 'int main ()': source.cpp: 30: 8: error: no matching function for call to 'g (Foo &)' source.cpp: 30: 8: note: candidate is: source.cpp: 18: 6: note: template <class type> void g (const Base <type, int> &) source.cpp: 18: 6: note: template argument deduction / substitution failed: source.cpp: 30: 8: note: mismatched types 'int' and 'void' source.cpp: 30: 8: note: 'Foo' is not derived from 'const Base <type, int>'Even better than MSVC!
template<typename type> void get_tag(type*); //default tag is 'void' template<typename t> struct base_tag { typedef decltype(get_tag((t*)42)) type; }; template<typename type, typename tag = typename base_tag<type>::type> struct Base { friend tag get_tag(type*); //never defined }; struct Foo : Base<Foo> //tag defaults to void {};
struct Foo : Base<Foo, void> {};
decltype
keyword. But if you have a finite number of tags (or whatever), you can use the trick with sizeof
( sizeof
trick): struct tag1 {}; //a set of tags struct tag2 {}; struct tag3 {}; #define REGISTER_TAG(tag, id) char (&get_tag_id(tag))[id];\ template<> struct tag_by_id<id>\ { typedef tag type; }; template<unsigned> struct tag_by_id; REGISTER_TAG(tag1, 1) //defines id's REGISTER_TAG(tag2, 2) REGISTER_TAG(tag3, 42) template<typename t> struct base_tag { enum { tag_id = sizeof(get_tag_id(get_tag((t*)42))) }; typedef typename tag_by_id<tag_id>::type type; }; template<typename type, typename tag = typename base_tag<type>::type> struct Base { friend tag get_tag(type*); //never defined }; template<typename type> void f(const Base<type>&) {} struct Foo : Base<Foo, tag1> {}; int main() { Foo foo; f(foo); }
template<class type1, class tag1, class type2, class tag2> void h(const Base<type1, tag1>&, const Base<type2, tag2>&) {}
class
keyword does not make the code much shorter. template<class type1, class type2> void h(const Base<type1>&, const Base<type2>&) {}
std::vector, ( ), " std::vector
() std::allocator
". , (, ), . , , . , , .
, , - .
std::vector, ( ), " std::vector
() std::allocator
". , (, ), . , , . , , .
, , - .
std::vector, ( ), " std::vector
() std::allocator
". , (, ), . , , . , , .
, , - .
std::vector, ( ), " std::vector
() std::allocator
". , (, ), . , , . , , .
, , - .
Source: https://habr.com/ru/post/152387/
All Articles