Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Stroustrup's Can_Copy template work?

Tags:

c++

templates

Stroustrup provides a Can_copy template. How does it work?

template<class T1, class T2> struct Can_copy {
    static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
    Can_copy() { void(*p)(T1,T2) = constraints; }
};

In particular, why does he need the line void(*p)(T1,T2) = constraints; instead of an empty constructor? Are compilers allowed to produce only the functions a particular template instance uses as an optimisation?

like image 871
Casebash Avatar asked Feb 21 '11 23:02

Casebash


4 Answers

It's because not used member functions in templates don't exist in generated code, so to check constrainst you would have to call constraints() explicitly somewhere.

This way code for constraints() is generated, so constraints are checked in compile time (and that is the purpose of Can_copy).

like image 188
Pawel Zubrycki Avatar answered Nov 16 '22 19:11

Pawel Zubrycki


C++03 §14.7.1p1:

Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; …

Thus, this code cannot instantiate Can_copy::constraints:

template<class T1, class T2>
struct Can_copy {
  static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
  Can_copy() { /* note the change here */ }
};

template<class Container>
void draw_all(Container& c) {
  typedef typename Container::value_type T;
  Can_copy<T,Shape*>(); // accept containers of only Shape*s
}

But, in the original code, when Can_copy's ctor is instantiated, as it must be when it is used, its body (definition) is as well, and that triggers instantiation of Can_copy::constraints.

You can see this same problem in reverse, where an explicit instantiation gives you everything, even something you don't want:

typedef std::list<int>::iterator Iter;
std::reverse_iterator<Iter> x;  // Implicit instantiation; works.

template std::reverse_iterator<Iter>;
// Explicit instantiation; fails to instantiate op+ and other operators used
// for random access iterators from reverse_iterator.
like image 44
Fred Nurk Avatar answered Nov 16 '22 20:11

Fred Nurk


Meant to be informative, not an answer (not deserving of an upvote):

In C++0x you will have improved facilities for this in the header <type_traits>.

std::is_copy_constructible<T1>::value;
std::is_copy_assignable<T1>::value;
std::is_constructible<T1, T2>::value;
std::is_assignable<T1, T2>::value;
std::is_convertible<T1, T2>::value;

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3225.pdf

search for "[meta]".

like image 3
Howard Hinnant Avatar answered Nov 16 '22 20:11

Howard Hinnant


The usage of the template is as simple as Can_copy<int, char>();

If an instance of T1 cannot be copied to an instance of T2, the code b = a wouldn't compile at all. The same way, if an instance of T2 cannot be initialized with the instance of T1, the code T2 c = a; wouldn't compile.

So, if T1 cannot be copied to T2, the line containing the template usage won't compile.

The whole construct produces (almost) no code, and is easily removed by the optimizer.

like image 2
Vlad Avatar answered Nov 16 '22 18:11

Vlad