Curiously recurring template pattern

I

iuweriur

A few questions on the curiously recurring template pattern:

This page:
http://c2.com/cgi/wiki?CuriouslyRecurringTemplate

this part:
template<typename T> struct ArithmeticType
{
T operator + (const T& other) const
{
T result(*this); // <--------- THIS LINE
result += other;
return result;
}
// etc.
};

In the above marked line, how comes that one doesn't need any type of
cast from *this to const T& ?

And if the cast is needed, what type of cast would you use?
I suppose that the reinterpret_cast would not be portable (in theory, or
also in practice?) and anyway does not handle cases with MI, and the
dynamic_cast is slow and works only on polimorphic types, right?




Another question on this page:
http://www.informit.com/articles/article.asp?p=31473&seqNum=3

At the end there is written:
----
In general, CRTP is useful to factor out implementations of interfaces
that can only be member functions (for example, constructor,
destructors, and subscript operators).
----

For the subscript I can maybe understand, but how can you do it for
constructors and destructors?


Thank you
iuweriur
 
A

Alf P. Steinbach

* iuweriur:
A few questions on the curiously recurring template pattern:

This page:
http://c2.com/cgi/wiki?CuriouslyRecurringTemplate

this part:
template<typename T> struct ArithmeticType
{
T operator + (const T& other) const
{
T result(*this); // <--------- THIS LINE
result += other;
return result;
}
// etc.
};

In the above marked line, how comes that one doesn't need any type of
cast from *this to const T& ?

Presumably the intent is that class T has a public constructor that takes
an ArithmeticType<T> argument and uses dynamic_cast down to T.

I think that's ugly, and doing a downcast in ArithmeticType is also ugly.

Downcasting is what virtual functions are all about achieving in a type-
safe and efficient manner, so I'd write


template<typename T>
class ArithmeticType
{
protected:
virtual T const& thisT() const = 0;

T operator + (const T& other) const
{
T result( thisT() );
result += other;
return result;
}
// etc.
};


And if the cast is needed, what type of cast would you use?

No cast needed; see above.

Another question on this page:
http://www.informit.com/articles/article.asp?p=31473&seqNum=3

At the end there is written:

A set of classes may have very similar constructors or constructor parts,
differing only in the concrete types involved.

Such a constructor can be factored out in a CRT base class.

For example, the CRT base class might be Window, and a typical derived
class Button, and Button might have a static function nativeWindowHandle()
that the Window class constructor calls; see FAQ 23.4 for other techniques
to do just about the same without using the CRT pattern.
 
J

Jonathan Turkanis

Alf P. Steinbach said:
* iuweriur:

Presumably the intent is that class T has a public constructor that takes
an ArithmeticType<T> argument and uses dynamic_cast down to T.

A static_cast should do the trick here. I think it's required in the
above example. Maybe the authors were using VC6.

Jonathan
 
J

Jonathan Turkanis

A static_cast should do the trick here. I think it's required in the
above example. Maybe the authors were using VC6.

Whoops. VC6 needs the cast too.
 
A

Alf P. Steinbach

* Jonathan Turkanis:
A static_cast should do the trick here.

Keeping in mind that no cast is needed if the scheme shown in my earlier
posting is used: no, the reason I wrote dynamic_cast, not static_cast, is
that the (presumed) constructor is public, and so cannot know what kind of
beast it's passed. Of course ArithmeticType then needs at least one
virtual function.
 
I

iuweriur

Jonathan said:
A static_cast should do the trick here. I think it's required in the
above example. Maybe the authors were using VC6.

Jonathan


Hey how can you use static_cast to downcast like this?
When the compiler compiles the ArithmeticType, *this is not guaranteed
to be of type T as far as the compiler knows

Am I wrong?
 
J

John Harrison

Hey how can you use static_cast to downcast like this?
When the compiler compiles the ArithmeticType, *this is not guaranteed
to be of type T as far as the compiler knows

Am I wrong?

Isn't that the whole point of static_cast?

If the CRTP is being used correctly *this will be as base class of type T
and the static_cast will work. If CRTP is being used incorrectly then
*this will not be a base class of T and you will get a compile error.

john
 
A

Alf P. Steinbach

* John Harrison:
Isn't that the whole point of static_cast?

If the CRTP is being used correctly *this will be as base class of type T
and the static_cast will work. If CRTP is being used incorrectly then
*this will not be a base class of T and you will get a compile error.

Well I'm wondering about that: how does it work with 'export'? <g/>

Okay I don't have a compiler that supports 'export' so don't know much
about it, but if it's able to compile templates separately then it
seems that the _linker_ must detect the invalid static_cast.

So, is that detection required by the standard (if so, where), and are
linkers really that smart?
 
R

Rob Williscroft

Alf P. Steinbach wrote in in
comp.lang.c++:
Well I'm wondering about that: how does it work with 'export'? <g/>

What about export makes you think it won't work ?
Okay I don't have a compiler that supports 'export' so don't know much
about it, but if it's able to compile templates separately then it
seems that the _linker_ must detect the invalid static_cast.

The "compilation" done by export is only half the "compilation" that
needs to be done. A compiler that implements export must do the
second half (instantiation) at _link_ time.
So, is that detection required by the standard (if so, where), and are
linkers really that smart?

No the compiler (whenever it compiles) detects the errors:

template < typename A, typename B >
A &my_cast( B &b )
{
return static_cast< A & >( b );
}

struct base {} b;
struct derived : base {} d;

int main()
{
base &b = d;
derived &d = my_cast< derived >( b );

int &i = my_cast< int >( d ); /* <-- error */
}

If my_cast<>() were exported then the export mechanism would need to
be told sufficient information about base and derived so that it could
be instantiated, and sufficient info' about derived so it knows it can't
do the static_cast< int & >, there is no difference between the exported
code calling static_cast<>, or operator + or trying to invoke A::foo().

When the compiler instantiates the exported code it need's to
callback (if you like) to the main compile to get all the info'
about the paramiter types.

Rob.
 
A

Alf P. Steinbach

* Rob Williscroft:
Alf P. Steinbach wrote in in
comp.lang.c++:


What about export makes you think it won't work ?

The reason I'm wondering was given in the next paragraph, cited below:

The "compilation" done by export is only half the "compilation" that
needs to be done. A compiler that implements export must do the
second half (instantiation) at _link_ time.

By studying your answers below I think I see what's going on. It's
not instantiation at _link_ time but instantiation that uses precompiled
information. In other words, the compiler needs access to the compiled
form of the template, not just the header file -- and if that's the
case then 'export' seems to be a very limited feature.

The alternative is that the linker invokes a partial compilation, or at
least class hierarchy information, but below you answer "no" to that?
 
R

Rob Williscroft

Alf P. Steinbach wrote in in
comp.lang.c++:
The reason I'm wondering was given in the next paragraph, cited below:



By studying your answers below I think I see what's going on. It's
not instantiation at _link_ time but instantiation that uses
precompiled information. In other words, the compiler needs access to
the compiled form of the template, not just the header file -- and
if that's the case then 'export' seems to be a very limited feature.

Yes, I think it promises faster compile times, and also the exported
code that isn't in a header (the defenition's) gets protected from
#include order and #define issues, though both of these might affect
the specialization's found and overloads found by ADL.
The alternative is that the linker invokes a partial compilation, or
at least class hierarchy information, but below you answer "no" to
that?

Well it can "precompile" any bits that are non-dependant, say:

// "exported.hpp"
export template < typename T > struct X
{
int foo();
T bar();
};

// EOF

// "exported.cpp"
#include "exported.hpp"
#include <string>
using namespace std;

int X< T >::foo()
{
return 1;
}

template < typename T >
T X< T >::bar()
{
return T( foo(), string( "bar" ) );
}

X< T >::foo() could be fully compiled, but X< T >::bar() would
need to be in some itermediate form.

However the call's to X< T >::foo() and
std::string::string( char const * ) would be fully resolved and fixed.

Also std::basic_string< char > (aka std::string) could be instantiated
(unless its also exported).

// "main.cpp"

#include <string>
struct string
{
string( char const * ) {}
};

struct Y
{
int ii;
std::string ss;
Y( int i, std::string s ) : ii( i ), ss( s ) {}
};

#include "exported.hpp"

int main()
{
X< Y > x( 0, "" );
X< Y > y( x.bar() );
}

AFAICT the compilation of "main.cpp" would have to export the
declaration's and defenition's of Y<>, so that the linker/compiler
could actually instantiate X< Y >.

Or maybe when compiling "main.cpp" the compiler would /import/
whatever was exported by compiling "exported.cpp":

14/9 [Note: an implementation may require that a translation
unit containing the definition of an exported template be compiled
before any translation unit containing an instantiation of that
template. ]

The more I type (into this post) the more I'm becoming convinced
that any compilation-speedups export might give us ara a *fragile*
thing at best :(.

Rob. -- http://www.victim-prime.dsl.pipex.com/
 
J

Jonathan Turkanis

Alf P. Steinbach said:
* Jonathan Turkanis:

Keeping in mind that no cast is needed if the scheme shown in my earlier
posting is used: no, the reason I wrote dynamic_cast, not static_cast, is
that the (presumed) constructor is public, and so cannot know what kind of
beast it's passed. Of course ArithmeticType then needs at least one
virtual function.

I understand. But dynamic_cast should not be required with CRTP, which
is supposed to be as efficient as if the code from the base were
hand-written in the derived class.

I often write a function self() in the base class which returns *this
cast to the derived type.

Jonathan
 
A

Alf P. Steinbach

* Jonathan Turkanis:
I understand. But dynamic_cast should not be required with CRTP, which
is supposed to be as efficient as if the code from the base were
hand-written in the derived class.

I often write a function self() in the base class which returns *this
cast to the derived type.

Well the point may be academic (whether to use pure virtual self() or
non-virtual inline self() with static_cast is personal preference ++),
but I think for completeness it should be noted that static_cast is
not 100% type-safe here. I.e., it does not _guarantee_ a compilation
error in case of incorrect usage. At least not with VC 7.1, which, by
means of threats of giving it some heavy template code, I succeded in
getting to accept the following code which is incorrect as can be:


#include <iostream>

template<typename T>
class ArithmeticType
{
public:
T operator+ (const T& other) const
{
T result( *static_cast<T const*>( this ) );
result += other;
return result;
}
// etc.
};

class Fraction: public ArithmeticType<Fraction>
{
private:
int x, y; // Represents x/y.
public:
Fraction(): x( 1234 ), y( 9999 ) {}
Fraction& operator+=( Fraction const& other )
{
// Whatever.
int something = x + y;
return *this;
}
};

struct UnboundedInt {};

// This ingenious new class provides _unbounded_ precision fractions! :)
class Fractionn: public ArithmeticType<Fraction>
{
private:
UnboundedInt x, y; // Represents x/y.
public:

Fractionn() {}
Fractionn( Fraction const& ) {} // Conversion Fraction to Fractionn
operator Fraction() const { return Fraction(); } // Other way.

Fractionn& operator+=( Fractionn const& other )
{
// Whatever.
return *this;
}
};

int main()
{
Fractionn a, b;
Fractionn c = a + b;
}
 
J

Jonathan Turkanis

Alf P. Steinbach said:
* Jonathan Turkanis:

Well the point may be academic (whether to use pure virtual self() or
non-virtual inline self() with static_cast is personal preference
++),

I disagree. Using virtual functions could make the base class unusable
for many applications. Take Boost.Operators, for example
(http://www.boost.org/libs/utility/operators.htm), which uses CRTP for
exactly the same purpose as the OP's example. If Boost.Operators used
virtual functions instead of static_casts it would be useless for
defining numeric types such as Boost.Rational, where performance is
paramount.

Of course, for windows and buttons it probably makes no difference.
but I think for completeness it should be noted that static_cast is
not 100% type-safe here. I.e., it does not _guarantee_ a compilation
error in case of incorrect usage.

True. Unfortunately, I don't think static_asserts work in this case.
Therefore, where performance is important, we'll have to settle for
documentation which states, for example, that

struct X : ArithmeticType<Y> { };

is an error if X != Y.

Jonathan
 
A

Alf P. Steinbach

* Jonathan Turkanis:
I disagree. Using virtual functions could make the base class unusable
for many applications. Take Boost.Operators, for example
(http://www.boost.org/libs/utility/operators.htm), which uses CRTP for
exactly the same purpose as the OP's example. If Boost.Operators used
virtual functions instead of static_casts it would be useless for
defining numeric types such as Boost.Rational, where performance is
paramount.

That's in the "++". In some cases performance means using unsafe
features. On the other hand, boost::lexical_cast goes to the opposite
extreme end to provide type-safety by sacrificing performance, so clearly
there is preference involved in many cases... ;-)
 
J

Jonathan Turkanis

Alf P. Steinbach said:
* Jonathan Turkanis:

That's in the "++".

I guess I don't know what that means.
In some cases performance means using unsafe
features. On the other hand, boost::lexical_cast goes to the opposite
extreme end to provide type-safety by sacrificing performance, so clearly
there is preference involved in many cases... ;-)

I'm certainly not going to argue that you should never give up
performance for saftey. I've just never considered CRTP an unsafe
idiom.

Jonathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top