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