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.
Related
Take the following example (https://godbolt.org/z/ouX3Vz):
template<typename T>
using A = T;
template<typename T, typename V>
using A = V; // Why is this not allowed?
template<typename T>
void B() {}
template<typename T, typename V>
void B() {} // Yet this is allowed?
int main() {
A<int> hello = 10; // Allowed, T=int
A<double> world = 20.0; // Allowed, T=double
// A<int, int> bad = 20; // Not allowed, T=int, V=double?
B<int>();
B<int, int>();
}
We are allowed to have two function templates for B as the parameters differ, however, we are not allowed to have two alias templates for A despite differing parameters.
Is this an oversight in the standard or is there a rationale that I am missing? Are there any references to the standard describing this behavior?
You're allowed to define multiple function templates with the same name because functions can be overloaded with each other. If functions were allowed to overload but function templates were not, then it would be a serious obstacle in the way of using templates.
There was no need to allow multiple class templates in the same scope to have the same name, as there are not many use cases for such a feature that are not already solved by a single variadic template, and it would make the language more complicated. (Consider for example the inability to later refer to one particular template from the set.) A similar statement applies to alias 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?
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.
template <size_t N>
class Foo
{
static_assert(N > 0, "WRONG");
//void Something() = 0; //my original implementation
};
int main() {
Foo<0> *p2 = nullptr; //no error
Foo<0> p; //gives an error
return 0;
}
I've tested both the lines separately. static_assert is not called when p2 is initialized but it is called and does indeed fail on p. Is this intended? (I've tried it on gcc, clang and VC)
What are the workarounds? Since I'm using abstract templated classes, it would be a nightmare if the assertion is only performed when a non-pointer object is instantiated. I can use a factory but that isn't exactly a proper solution.
You assuredly saw this quote from §14.7.1/1:
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.
Pointer types do not require their pointee to be a complete type (e.g. void* is an example of this). Thus the first line will not instantiate the specialization, but the second one needs to, hence the assertion fires only on that one.
This is also addressed by an example three paragraphs further down:
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<double>* q; // instantiation of class Z<double> not required
//[…]
}
Nothing in this example requires class Z<double> […] to be implicitly instantiated. — end example ]
What is meant with "deferred instantiation" in C++ templates?
Deferred instantiation is when the template is not instantiated until the corresponding entity is used for the first time. For example, you have a templated function:
template<int Size>
void YourFunction()
{
//does something
}
parameter Size can have any possible value that int can have. Do you automatically have the templated function instantiated for all possible values of Size? No, the template is only instantiated for the values that are actually used as the parameter when the function call first appears in the code:
YourFunction<100>(); //instantiated for 100
I have only heard people use the term "deferred instantiation" to refer to the situation where a class member definition is instantiated only if it is used
template<typename T>
struct A {
void f() {
T a; // invalid if T is void
}
};
A<void> a; // valid!
In this case, A<void> is implicitly instantiated because the compiler needs to know its size (formally, the class type needs to be complete, so an instantiation is triggered). But the instantiation of its member definitions are deferred until they are actually used. This does not only apply to member functions, but also to static data members and nested classes
struct Print {
Print() { std::cout << "hello!"; }
};
template<typename T>
struct A {
static Print print;
};
template<typename T>
Print A<T>::print;
Now even if you implicitly instantiate A<T> the message won't be printed until you explicitly refer to A<T>::print and use it. Explicit instantiation won't defer instantiation of member definitions - so the following will always print the message
template struct A<void>;
There is a trick to trigger instantiation of member definitions for implicit instantiations though: Refer to them in declaration parts of a class' member, like in the following changed class template
template<typename T, T&> struct useit { };
template<typename T>
struct A {
static Print print;
typedef useit<Print, print> useit_type;
};
Now if A<T> is implicitly instantiated the message is printed, because the typedef declaration refers to it.
I just found that when it comes to templates this code compiles in g++ 3.4.2 and works unless m() is not called:
template <typename T>
class C
{
T e;
public:
C(): e(0) {};
void m()
{
e = 0;
};
};
Now one may create and use instance
C<const int> c;
Until c.m() is not called there are no compile errors but is this legal?
Yes, this is legal. The template specification is that until a method is instantiated, it doesn't exist and therefor is not checked by the compiler. Here's the relevant bit from the spec:
14.7.1 - Implicit instantiation
-9- An implementation shall not implicitly instantiate a function
template, a member template, a
non-virtual member function, a member
class or a static data member of a
class template that does not require
instantiation.