开发者

How does Stroustrup's Can_Copy template work?

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?


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).


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.


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]".


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜