C training datascope lawrence d antonio
Download
1 / 137

C++ Training Datascope Lawrence D’Antonio - PowerPoint PPT Presentation


  • 88 Views
  • Uploaded on

C++ Training Datascope Lawrence D’Antonio. Lecture 7 An Overview of C++: What is Polymorphism? – Parametric Polymorphism. What is polymorphism?. Parametric. Universal. Subtype. Polymorphism. Overloading. Ad-hoc. Coercion.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' C++ Training Datascope Lawrence D’Antonio' - tieve


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
C training datascope lawrence d antonio

C++ TrainingDatascopeLawrence D’Antonio

Lecture 7

An Overview of C++:

What is Polymorphism? –

Parametric Polymorphism


What is polymorphism
What is polymorphism?

Parametric

Universal

Subtype

Polymorphism

Overloading

Ad-hoc

Coercion

Different types of objects respond to the same message and use the appropriate method.


Parametric polymorphism
Parametric Polymorphism

  • Parametric polymorphism parametrizes the object type (e.g., a list class, where the type of object stored is parametrized).

  • Parametric polymorphism in C++ is implemented as templates.

  • Both classes and functions may be templates.


Template functions
Template Functions

template<class T>

T max(T a, T b) {

return a > b ? a : b;

}


Is this legal
Is this legal?

int a,b = 6;

const int c = 3;

double x,y = 3.2;

a = max(b,4);

a = max(b,c);

x = max(y,3.3);

x = max(b,y);


a = max(b,4); //Legal, max<int,int>

a = max(b,c); //Legal, max<int,int>

x = max(y,3.3); //Legal, max<double,double>

x = max(b,y); //Illegal, no max<int,double>

A template function is called only when there is an exact match for type parameters (only trivial conversions, such as const int to int are allowed). But the following is legal.

x = max<double>(4,4.2);


Better max
Better max?

template<class S, class T>

T max(S a, T b) {

return a > b ? a : b;

}

main() {

int a, b = 3;

double x, y = 3.2;

a = max(b,5);

x = max(y,5.4);

x = max(b,y);

x = max(y,b);

return 0;

}


a = max(b,5);

//Legal, returns 5

x = max(y,5.4);

//Legal, returns 5.4

x = max(b,y);

//Legal, 3.2

x = max(y,b);

//Legal, but

//returns 3.0!


Best max
Best max?

template<class R, class S, class T>

R max(S a, T b) {

return a > b ? a : b;

}

main() {

int a, b = 3;

double x, y = 3.2;

a = max(b,5);

x = max(y,5.4);

x = max(b,y);

x = max(y,b);

return 0;

}


Doesn’t compile. The function max() is supposed to have 3 template parameters. But each call only uses 2 parameters.


Try this max
Try this max template parameters. But each call only uses 2 parameters.

template<class R, class S, class T>

R max(S a, T b) {

return a > b ? a : b;

}

main() {

int a, b = 3;

double x, y = 3.2;

a = max<int>(b,5);

x = max<double>(y,5.4);

x = max<double>(b,y);

x = max<double>(y,b);

return 0;

}


Is this legal1
Is this legal? template parameters. But each call only uses 2 parameters.

  • Return back to the original definition of max.

    int x = 5, y = 6;

    int *p = &x, *q = &y;

    int z = max(p,q);


Legal, but probably not what you want. template parameters. But each call only uses 2 parameters.

max(p,q) compares addresses, not data values.


Can we fix this
Can we fix this? template parameters. But each call only uses 2 parameters.

template<class T>

T max(T a, T b)

{ return a > b ? a : b; }

template<class T>

T* max<T *a, T *b>

{ return *a > *b ? a : b; }


Should we fix this
Should we fix this? template parameters. But each call only uses 2 parameters.

  • Probably not. Overloading the max function to compare dereferenced pointers means that we can’t compare addresses using max.


Stl version of max
STL version of template parameters. But each call only uses 2 parameters.max

template<class T>

const T &max(const T &a, const T &b)

{ return a < b ? b : a; }


Another problem
Another problem template parameters. But each call only uses 2 parameters.

const char *s = “Hello”;

const char *t = “World”;

std::string s = max(s,t);

std::cout << s;

What does this print out?

Who knows? max returns the char* with the larger memory address.


A solution
A Solution template parameters. But each call only uses 2 parameters.

  • Overload the max function. The second is called a template specialization.

    template<class T>

    const T& max(const T&a, const T&b)

    { return a < b ? b : a; }

    const char* max(const char *a,const char* b)

    { return std::strcmp(a,b) < 0 ? b : a; }


Stl solution
STL Solution template parameters. But each call only uses 2 parameters.

template<class T>

const T& max(const T&a, const T&b)

{ return a < b ? b : a; }

template<class T, class BinPred>

const T& max(const T&a, const T&b, BinPred comp)

{ return comp(a,b) ? b : a; }


Using a predicate
Using a predicate template parameters. But each call only uses 2 parameters.

bool comp(const char *a, const char *b)

{

return std::strcmp(a,b) < 0;

}

const char *s = “Hello”;

const char *t = “World”;

std::string s = max(s,t,comp);


Functor solution
Functor solution template parameters. But each call only uses 2 parameters.

class Comp {

public:

bool operator()(const char *a,

const char *b)

{ return std::strcmp(a,b) < 0; }

};

const char *s = “Hello”;

const char *t = “World”;

std::string s = max(s,t,Comp());


Is this legal2
Is this legal? template parameters. But each call only uses 2 parameters.

std::string s1(“apple”);

std::string s2(“tomato”);

std::max(“apple”,”peach”);

std::max(“apple”,”tomato”);

std::max(“apple”,s1);

std::max(s1,s2);


std::max(“apple”,”peach”); template parameters. But each call only uses 2 parameters.

//Legal, both arguments are const char[5]

std::max(“apple”,”tomato”);

//Illegal, arguments are different types

std::max(“apple”,s1);

//Illegal, arguments are different types

std::max(s1,s2);

//Legal, both arguments are type string


Is this legal3
Is this legal? template parameters. But each call only uses 2 parameters.

template<class T>

T foo()

{ return T(); }

template<class T>

void bar()

{

T t;

}

main() {

int x = foo();

bar();

return 0;

}


Not legal for two reasons. template parameters. But each call only uses 2 parameters.

int x = foo();

Illegal because the compiler doesn’t know which version of foo() to call. It won’t determine that foo() should return an int by looking at the LHS.

bar();

Illegal because the compiler doesn’t know which version of bar() to call.


Legal version of example
Legal version of example template parameters. But each call only uses 2 parameters.

template<class T>

T foo()

{ return T(); }

template<class T>

void bar()

{

T t;

}

main() {

int x = foo<int>();

bar<int>();

return 0;

}


Template name lookup
Template name lookup template parameters. But each call only uses 2 parameters.

  • Template name resolution involves what is known as “two-phase name lookup”.

  • Template names are divided into two categories: dependent and non-dependent names.

  • Dependent and non-dependent names are resolved at different times.


Dependent names
Dependent names template parameters. But each call only uses 2 parameters.

  • Dependent names have definitions that depend on template parameters, but have no declaration within the template definition.

  • Dependent names are only resolved at the time of instantiation.


Non dependent names
Non-dependent names template parameters. But each call only uses 2 parameters.

  • Non-dependent names are names that don’t depend on a template parameter, the name of the template itself, and names declared within it (members, friends, and local variables).


Example
Example template parameters. But each call only uses 2 parameters.

template<class T>

class X {

T t;

public:

X(const T &a):t(a) { t.init(); }

template<class U>

T foo() {

U u = t.begin();

return *u;

}

};


Name resolution in example
Name resolution in example template parameters. But each call only uses 2 parameters.

  • Non-dependent names

    X, t, a, X(const T &), foo(), U,

    u

  • Dependent names

    T::init(), T::begin()


Sfinae
SFINAE template parameters. But each call only uses 2 parameters.

  • “Substitution failure is not an error.”

  • When examining which template function to call from a set of overloaded template functions, there is no error if some substitutions are illegal.


Example1
Example template parameters. But each call only uses 2 parameters.

template<class Func, class T>

void apply(Func f, T x)

{ f(x); }

template <class T>

void multi(T) { }

template <class T*>

void multi(T *) { }

main() {

apply(multi<int>,5);

return 0;

}


apply(multi<int>,5) template parameters. But each call only uses 2 parameters. calls multi(T) with int substituting for T. The failure of the substitution of int in multi(T*) does cause an error.


Is this legal4
Is this legal? template parameters. But each call only uses 2 parameters.

template<int N>

int g() { return N; }

template<int *P>

int g() { return *P; }

main()

{

return g<1>();

}


Yes, this is legal (if odd looking). template parameters. But each call only uses 2 parameters.g<1> binds to g<int>, the failure to bind to g<int *> is not an error.


Is this legal5
Is this legal? template parameters. But each call only uses 2 parameters.

template <class Alloc>

class container_helper

{

typedef Alloc::value_type value_type;

};


Not legal, the compiler has no idea what the dependent name Alloc::value_type represents. Here is the correct version.

template <class Alloc>

class container_helper

{

typedef typename Alloc::value_type

value_type;

};


Is this legal6
Is this legal? Alloc::value_type represents. Here is the correct version.

template <class Alloc>

class container_helper

{

typedef typename Alloc::value_type

value_type;

typedef

std::pair<value_type,value_type> element_type;

};


Yes, this is legal. Alloc::value_type represents. Here is the correct version.

typedef std::pair<value_type,value_type>

element_type;

This can be resolved in scope.


Is this legal7
Is this legal? Alloc::value_type represents. Here is the correct version.

template <class Alloc>

class container_helper

{

typedef typename Alloc::value_type

value_type;

typedef

std::pair<value_type,value_type> element_type;

typedef typename

Alloc::rebind<element_type>::other

element_allocator;

};


Not legal, perhaps surprisingly. The compiler cannot determine from

Alloc::rebind<element_type>::other

what rebind refers to (is it an object, a function, or Superman?).


ADL determine from

  • Argument-dependent lookup applies to unqualified names where it appears that a nonmember function is being called.

  • ADL proceeds by looking up a name in namespaces and classes associated with the types of the function call arguments.


Which functions are called
Which functions are called? determine from

#include <iostream>

namespace X {

template <class T>

void f(T) { std::cout << "f<T>\n"; }

}

namespace N {

using namespace X;

enum E{ e1 };

void f(E) { std::cout << "f(E)\n"; }

}

void f(int) { std::cout << "f(int)\n"; }

main() {

::f(N::e1);

f(N::e1);

return 0;

}


::f(N::e1); determine from

//Qualified name, so calls global f, no ADL used

f(N::e1);

//Calls N::f. The call argument N::e1 is associated

//with namespace N. So this means that N::f is //preferred over ::f. Note that X::f is not considered, //because using directives are ignored in ADL.


Is this legal8
Is this legal? determine from

template<typename T,

typename Alloc = std::allocator<T> > class my_container : private

container_helper<Alloc>::element_allocator

{

//...

};


Perhaps surprisingly this is legal. The dependent name determine from

container_helper<Alloc>::element_allocator

cannot be resolved,. But since it is being used as a base class, the compiler is happy.


What is rebind
What is rebind? determine from

  • A template typedef.

    template <class T> class allocator { . . .

    template <class U>

    struct rebind

    { typedef allocator<U> other; };

    ...

    template <class U>

    allocator(const allocator<U>&);

    };


Use of rebind
Use of rebind determine from

template <class T, class Allocator = allocator<T> >

class list {

private:

typedef . . . listnode;

typedef typename Allocator::rebind<listnode>::other

Node_allocator;

Node_allocator alloc_;

list_node* make_node()

{ return new(alloc_.allocate(1)) list_node; }

public:

list(const Allocator& a = Allocator()) : alloc_(a) { } // implicit conversion . . .

};


Template classes
Template classes determine from

  • The declaration and definition of a template class “must” be in the same header file.


For example
For example determine from

//stack.h

template <class T>

class Stack {

public:

T pop();

};


//stack.cpp determine from

#include “stack.h”

template <class T>

T Stack<T>::pop()

{

//...

}


//main.cpp determine from

#include “stack.h”

main() {

Stack<int> si;

//Push elements onto si

int x = si.pop(); //Illegal!


What happened
What happened? determine from

  • No function Stack<int>::pop() defined.

  • When stack.cpp compiled, no definition for Stack<T> is created. This is because no instantiations exist.

  • Since main.cpp uses Stack<int>, the compiler instantiates a definition for Stack<int>, but not for the member functions in Stack.cpp!


Can we fix this1
Can we fix this? determine from

  • export keyword

    //stack.h

    export template <class T>

    class Stack {

    public:

    T pop();

    };


What does this do
What does this do? determine from

  • The keyword export allows the programmer to put template declarations and template definitions in different translation units.

  • PROBLEM: Few compilers implement export!


Another solution
Another solution determine from

  • Explicit instantiation. It requires knowledge of what template instantiations are needed in main.


Example2
Example determine from

//stack.h

#ifndef STACK_H

#define STACK_H

template <class T>

class Stack {

public:

T pop();

};

#endif


//mystackdef.h determine from

#ifndef MYSTACKDEF_H

#define MYSTACKDEF_H

#include <iostream>

#include "mystack.h"

template <class T>

T Stack<T>::pop() {

std::cout << "Pop\n";

return T();

}

#endif


//mystackinst.cpp determine from

#include "mystackdef.h"

#include <string>

template class Stack<int>;

template

std::string Stack<std::string>::pop();


//main.cpp determine from

#include "mystack.h"

#include <string>

main() {

Stack<int> s;

Stack<std::string> t;

s.pop();

t.pop();

return 0;

}


Does that work
Does that work? determine from

  • Yes, it works. But it is a burden on the programmer. They must see what specific instantiations are needed in the program.


Nontype template parameters
Nontype template parameters determine from

template <class T, int N>

class Stack

{

T arr[N];

int count;

public:

Stack(): count(0) {}

void push(const T &t);

};

template <class T, int N>

void Stack<T,N>::push(const T &t)

{

if (count != N)

arr[count++] = t;

}


Is this legal9
Is this legal? determine from

main() {

Stack<int,10> stack10;

Stack<int,20> stack20;

stack10.push(8); stack10.push(-3);

stack20.push(11); stack20.push(0);

stack20 = stack10;

return 0;

}


Not legal. determine from

stack20 = stack10;

Assignment between Stacks of different types.


Is this legal10
Is this legal? determine from

#include <algorithm>

#include <vector>

#include <list>

template <class T, int x>

T add_val(const T &t)

{ return t+x; }

main() {

std::vector<int> v;

std::list<int> l(2);

v.push_back(5); v.push_back(8);

std::transform(v.begin(),v.end(),

l.begin(), add_val<int,10>);

return 0;

}


Yes, this is legal. determine from

std::transform copies from vector v to list l, applying the function add_val<int,10> to each element being copied.


Nontype parameter rules
Nontype parameter Rules determine from

  • A nontype parameter must be one of the following types:

  • An integer or enumeration type

  • A pointer type

  • A reference type


Is this legal11
Is this legal? determine from

int C

class C;

int X;

template <class T>

class X;

struct S;

template <class T>

class S;


int C determine from

class C; //Legal, class and nonclass names

//live in different spaces

int X;

template <class T>

class X; //Illegal, name conflict

struct S;

template <class T>

class S; //Illegal name conflict


Template linkage
Template Linkage determine from

  • Class names can be the same as nonclass names

  • Class templates cannot share names with other program constructs. Templates have linkage, but not “C” linkage.


Is this legal12
Is this legal? determine from

template <class T>

class Foo {

public:

virtual ~Foo();

template <class U>

virtual void bar(const U&);

};


virtual ~Foo(); //Legal, one copy per Foo<T> determine from

//instance

template <class U>

virtual void bar(const U&);

//Illegal, unknown number of versions of

//bar() per Foo<T> instance

Conclusion: Member function templates cannot be virtual.


Is this legal13
Is this legal? determine from

template <class T>

class Foo {

private:

T t;

public:

Foo(T s):t(s) { }

T get() const { return t; }

template<class U>

Foo<T> operator=(const Foo<U> &x) {

if ( (void *) this == (void *) &x )

return *this;

t = x.get();

return *this;

}

};


What functions are called
What functions are called? determine from

main() {

Foo<int> f1(4);

Foo<double> f2(5.4);

f2 = f1;

Foo<int> f3(6);

f1 = f3;

return 0;

}


Foo<int> f1(4); //ctor determine from

Foo<double> f2(5.4); //ctor

f2 = f1; //User defined operator=

Foo<int> f3(6); //ctor

f1 = f3; //Compiler defined operator=


Template vs nontemplate
Template vs. Nontemplate determine from

#include<iostream>

template<class T>

void f(T)

{ std::cout << "Template\n"; }

void f(int)

{ std::cout << "Nontemplate\n"; }

main()

{

f(7);

f('a');

return 0;

}


f(7); determine from

//Calls f(int), in general the nontemplate //version will be preferred

f('a');

//Calls f<char>(char), prefers an exact match


Template template parameters
Template template parameters determine from

template <typename T,

template <typename U,

typename ALLOC = std::allocator<U> >

class CONT = std::deque>

class Stack {

private:

CONT<T> c;

public:

void push(const T &t);

void pop();

T top() const;


template <typename T2, determine from

template < typename U,

typename = std::allocator<U> >

class CONT2 = std::deque>

Stack<T,CONT> &operator=(Stack<T2,CONT2> const &s) {

if ((void *)this == (void *)&s) {

return *this

}

Stack<T2,CONT2> tmp(s);

c.clear();

while (!tmp.empty()) {

c.push_front(tmp.top());

tmp.pop();

}

return *this;

}

};


Usage
Usage determine from

Stack<int> a;

Stack<float> b;

// . . .

b = a;

Stack<int,vector<int> > c;

Stack<float,vector<float> > d;

// . . .

d = c;


Is this legal14
Is this legal? determine from

class X { };

list<::X> what;


Not legal. determine from

list<::X> what;

//This is the same as

list[:X> what;

//<: is a digraph that is the same as

//a [


Correct version
Correct version determine from

list< ::X> what;


Template argument deduction
Template Argument Deduction determine from

  • First, template parameters are deduced from the argument types in a function call

  • Next, if all parameters can be correctly deduced from the argument types then these types are used in the rest of the function declaration.

  • If either step above fails then a substitution failure occurs (but only an error if all substitutions fail).


Example3
Example determine from

template<class T>

typename T::value_type at(const T &a, int i)

{ return a[i]; }

void f(int *p)

{ at(p,0); }

void g(int *p)

{ at<int*>(p,0); }

main()

{

int a[3];

f(a);

g(a);

return 0;

}


This is clearly illegal. determine from

In

void f(int *p)

{ at(p,0); }

There is no matching function at(int *&,int)

In

void g(int *p)

{ at<int*>(p,0); }

There is no matching function at(int *&,int)


Argument parameter matching
Argument-parameter matching determine from

  • Suppose an actual type A is matched to a parameter type T.

  • If the argument parameter is a reference then P is the type referenced and A is the type of the argument.

  • Otherwise P is the declared parameter type and A is determined by decaying types (e.g., arrays go to pointers, const and volatile are ignored).


What type is t
What type is T? determine from

template<class T>

void f(T) {}

template<class T>

void g(T&) {}

main() {

double x[20];

const int y = 3;

f(x); g(x);

f(y); g(y);

f(3); g(3);

return 0;

}


f(x); // nonreference parameter, T is double* determine from

g(x); // reference parameter, T is double[20]

f(y); // nonreference parameter, T is int

g(y); // reference parameter, T is const int

f(3); // nonreference parameter, T is int

g(3); // reference parameter, ERROR, 3 is not int&


Deduced contexts
Deduced Contexts determine from

  • Complex type declarations are built up from elementary constructs.

  • Matching proceeds from the top level construct and recurses through the composing elements.

  • Type declarations matched in this way are called deduced contexts.

  • Qualified type names, e.g., Q<T>::X cannot be used to deduce T.


What are the deduced types
What are the deduced types? determine from

template<class T>

void f1(T*) {}

template<class T, int N>

void f2(T(&)[N]) {}

template<class T1, class T2, class T3>

void f3(T1 (T2::*)(T3*)) {}

struct S {

void f(double *) {}

};

void g(int ***p)

{

bool b[42];

f1(p);

f2(b);

f3(&S::f);

}


f1(p); determine from

//T is int**

f2(b);

//E is bool, N is 42

f3(&S::f);

//T1 is void, T2 is S, T3 is double


Is this legal15
Is this legal? determine from

template<int N>

class X {

public:

typedef int I;

void f(int) {}

};

template<int N>

void fppm(void (X<N>::*)(X<N>::I)) {}

main() {

fppm(&X<33>::f);

return 0;

}


No, determine from

The compiler cannot determine the type of the qualified expression

X<N>::I


Ok is this legal
OK, is this legal? determine from

template<int N>

class X {

public:

typedef int I;

void f(int) {}

};

template<int N>

void fppm(void (X<N>::*)(typename X<N>::I)) {}

main() {

fppm(&X<33>::f);

return 0;

}


Yes, this is legal (but complicated). determine from

How does it match types in the expression?

fppm(&X<33>::f);

X<N>::I is a nondeduced context, but the compiler can use the deduced context X<N>::* to determine the parameter N and then plug that into the nondeduced context.


Is this legal16
Is this legal? determine from

template<class T>

class B

{};

template<class T>

class D: public B<T>

{};

template<class T>

void f(B<T> *) {}

template<class T>

void g(D<T> d) {

f(&d);

}


Yes this is legal. determine from

f(&d) is legal because a D<T> can be converted to a B<T>


Which template is used
Which template is used? determine from

template<class T>

void f(T) {}

template<class T>

void f(T*) {}

main() {

f(0);

f((int*)0);

}


f(0); determine from

//Calls f<int>(int)

f((int*)0);

//Has two possibilities

//f<int*>(int*) or f<int>(int*)

//The second function is considered

//the “more specialized”


More specialized templates
More specialized templates determine from

  • In the previous example, consider possible argument types A1, A2*

  • A2* can match f<T>(T) (with T = A2*) or can match f<T>(T*) (with T = A2).

  • But A1 can only match f<T>(T) (with T = A1).

  • Hence f<T>(T*) is more specialized.


Full class specialization
Full Class Specialization determine from

template<class T>

class Types {

public:

typedef int I;

};

template<class T, class U = class Types<T>::I>

class S;

template<>

class S<void> { };

template<> class S<char,char>;

template<> class S<char,0>;


main() { determine from

S<int>* psi;

S<int> si;

S<void>* psv;

S<void,int> svi;

S<void,char> svc;

S<char,char> scc;

return 0;

}

template<>

class S<char,char> {};


Which are legal
Which are legal? determine from

template<>

class S<void> { };

//Legal, T = void, U = Types<void>::I

template<> class S<char,char>;

//Legal. T = char, U = char

template<> class S<char,0>;

//Illegal, T = char, but 0 is not a U


Which are legal1
Which are legal? determine from

S<int>* psi;

//Legal, uses S<T,U>, no definition needed

S<int> si;

//Illegal, uses S<T,U> but no definition //available

S<void>* psv;

//Legal, uses S<void>


Which are legal2
Which are legal? determine from

S<void,int> svi;

//Legal, uses S<void>, definition available

S<void,char> svc;

//Illegal, uses S<T,U>, no definition

S<char,char> scc;

//Illegal, no definition available


Is this legal17
Is this legal? determine from

template<>

class S<char**> {

public:

void foo() const;

};

template<>

void S<char**>::foo() const

{ }


Not legal. determine from

template<>

void S<char**>::foo() const

{ }

This is an “invalid function declaration”.

Correct syntax is

void S<char**>::foo() const

{ }


Is this legal18
Is this legal? determine from

template<class T>

class Outside {

public:

template<class U>

class Inside {};

};

template<>

class Outside<void> {

public:

template<class U>

class Inside {

static int x;

};

};

template<class U>

int Outside<void>::Inside<U>::x = 1;


(1) It’s okay that determine fromOutside<T>::Inside<U> and Outside<void>::Inside<U> are completely unrelated.

(2) A template class can have static members. Each instance of the class has it’s own version of the static member.

(3) The definition of Inside<U>::x is not preceded by template<>


Is this legal19
Is this legal? determine from

class Invalid {};

Invalid<double> x;

template<>

class Invalid<double>;


Not legal. determine from

Specialization of Invalid<double> after instantiation.


Is this legal20
Is this legal? determine from

#include<iostream>

template<class T>

int f(T, T x = 42) { return x; }

template<>

int f(int, int = 35) { return 0; }

template<class T>

int g(T, T x = 42) { return x; }

template<>

int g(int, int y) { return y/2; }

main() {

std::cout << g(0) << '\n';

return 0;

}


Not legal. A template function specialization cannot include default argument values.

template<class T>

int f(T, T x = 42) { return x; }

template<>

int f(int, int = 35) { return 0; }


Explicit specialization
Explicit Specialization default argument values.

  • Member templates, static data members, and member functions of class templates may be specialized.


Is this legal21
Is this legal? default argument values.

#include <iostream>

template<class T>

class Foo {

static int x;

public:

void bar() { std::cout << "Where am I?\n"; }

};

template<class T>

int Foo<T>::x = 4;

template<>

int Foo<int>::x;

template<>

void Foo<bool>::bar();

main() { return 0; }


This is legal (perhaps surprisingly). default argument values.

Static members and member functions can be given different definitions for specialized classes.

Nondefining out-of-class declarations are allowed for member functions or static data members.


Is this legal22
Is this legal? default argument values.

#include <iostream>

template<class T>

class Foo {

static int x;

public:

void bar() { std::cout << " Where am I?\n"; }

};

template<class T> int Foo<T>::x = 4;

template<>

class Foo<void> {

double y;

public:

void bar() { std::cout << x << (y = 3.415) << '\n'; }

};

main() {

Foo<void> fv;

fv.bar();

return 0;

}


Illegal. default argument values.

Foo<void> does not have a data member x.


Is this legal23
Is this legal? default argument values.

template<class T>

class Foo {

static int x;

public:

void bar() { std::cout << "Where am I?\n"; }

};

template<class T> int Foo<T>::x = 4;

template<> int Foo<int>::x;

template<> void Foo<bool>::bar();

main() {

Foo<int> fi;

fi.bar();

Foo<bool> fb;

}


Foo<int> fi; default argument values.

//Legal, Foo<int> doesn’t need to define

//static int x

fi.bar();

//Legal, Calls Foo<T>::bar()

Foo<bool> fb;

//Legal, Foo<bool> doesn’t need to define

//member function bar()


Example4
Example default argument values.

template<class T>

class Outer {

public:

template<class U>

class Inner {

private:

static int count;

};

};


Is this legal24
Is this legal? default argument values.

template<>

int Outer<void>::Inner::count = 7;

template<>

template<>

int Outer<void>::Inner<char>::count = 7;

template<>

template<>

class Outer<int>::Inner<double> {

public:

enum { ecount = 1 };

Inner() { std::cout << "Inner<double>\n"; }

};


template<> default argument values.

int Outer<void>::Inner::count = 7;

//Not legal. Inner must have a type.

template<>

template<>

int Outer<void>::Inner<char>::count = 7;

//Legal, but template classes have explicit

//specialization

template<>

template<>

class Outer<int>::Inner<double> { }

//Legal, Inner allowed to be redefined.


Is this legal25
Is this legal? default argument values.

template<>

template<class T>

class Outer<char *>::Inner {

public:

static long count;

};

template<>

template<class T>

long Outer<char *>::Inner<T>::count = 6L;


template<> default argument values.

template<class T>

class Outer<char *>::Inner {}

//Not legal, Inner should be Inner<T>

template<>

template<class T>

long Outer<char *>::Inner<T>::count = 6L;

//Legal, Inner<T> allowed to define count


Is this legal26
Is this legal? default argument values.

template<class T>

template<>

class Outer<T>::Inner<void>;


Not legal. default argument values.

template<> cannot follow after a template parameter list.


Is this legal27
Is this legal? default argument values.

template<class T = float, int i = 5>

class A {

public: A() {}

};

template<>

class A<> { public: A() {}

};

main() {

A<int,6> x;

A<> y;

return 0;

}


A<int,6> x; default argument values.

//Legal, T = int, i = 6

A<> y;

//Legal, uses default arguments,

//T = float, i = 5


Partial specialization
Partial Specialization default argument values.

template<class T>

class List {}; //Primary template

template<class T>

class List<T*> { //Partial specialization

private:

List<void*> impl;

public:

void append(T* p) { impl.append(p); }

};

template<>

class List<void*> { //Full specialization

public:

void append(void *p) { ... }

};


Further specializations
Further specializations default argument values.

template<class C>

class List<void * C::*> {//Partial specialization

public:

typedef void * C::* ElementType;

void append(ElementType pm);

};

template<class T, class C>

class List<T * C::*> { //Partial specialization

private:

List<void* C::*> impl;

public:

typedef T* C::* ElementType

void append(ElementType pm)

{ impl.append( (void* C::*)pm); }

};


Partial specialization rules
Partial Specialization Rules default argument values.

  • The arguments of the partial specialization must match in kind (type, nontype, template) with the correspnding parameters of the primary template.

  • No default arguments.

  • Nontype arguments must be nondependent or plain (no expressions)

  • Argument list must be different from the primary template.


Example5
Example default argument values.

template<class T, int i = 3>

class S; //primary template

template<class T>

class S<int, T>; //Illegal, parameter mismatch

template<class T = int>

class S<T,10>; //Illegal, no default arguments

template<int I>

class S<int, I*2>; //Illegal, no nontype expressions

template<class U, int W>

class S<U,W>; //Illegal, same as primary template


Template error messages
Template Error Messages default argument values.

#include <list>

#include <string>

#include <functional>

#include <algorithm>

main() {

std::list<std::string> l;

l.push_back("Hello");

l.push_back("World");

std::list<std::string>::iterator pos;

pos = find_if(l.begin(), l.end(),

std::bind2nd(std::greater<int>(),"A"));

return 0;

}


/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.5/../../../../include/c++/3.4.5/bits/stl_algo.h: In function `_InputIterator std::find_if(_InputIterator, _InputIterator, _Predicate, std::input_iterator_tag) [with _InputIterator = std::_List_iterator<std::string>, _Predicate = std::binder2nd<std::greater<int> >]':

/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.5/../../../../include/c++/3.4.5/bits/stl_algo.h:336: instantiated from `_InputIterator std::find_if(_InputIterator, _InputIterator, _Predicate) [with _InputIterator = std::_List_iterator<std::string>, _Predicate = std::binder2nd<std::greater<int> >]'

error.cpp:14: instantiated from here

/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.5/../../../../include/c++/3.4.5/bits/stl_algo.h:187: error: no match for call to `(std::binder2nd<std::greater<int> >) (std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'

/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.5/../../../../include/c++/3.4.5/bits/stl_function.h:440: note: candidates are: typename _Operation::result_type std::binder2nd<_Operation>::operator()(const typename _Operation::first_argument_type&) const [with _Operation = std::greater<int>]

/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.5/../../../../include/c++/3.4.5/bits/stl_function.h:446: note: typename _Operation::result_type std::binder2nd<_Operation>::operator()(typename _Operation::first_argument_type&) const [with _Operation = std::greater<int>]


ad