Sign in to follow this  
yacwroy

reference to polymorphic type as template specialization

Recommended Posts

I have a trivial polymorphic set of classes (CBar inherits from CFoo). I have a template function that can take any type. I want to specialize this for CFoo and its subclasses.
#include <iostream>

class CFoo
  {
    public:
      virtual void foo() const
        { std::cout << "CFoo::foo()" << std::endl; }
  };

class CBar : public CFoo
  {
    public:
      virtual void foo() const
        { std::cout << "CBar::foo()" << std::endl; }
  };

template <typename tTYPE>
void zug(const tTYPE& var)
  {
    var.foo();
  }

void zug(const CFoo& var)
  {
    std::cout << "zug(tINT, const CFoo&)" << std::endl;
  }

template <typename tTYPE>
void twe(const tTYPE* var)
  {
    var->foo();
  }

void twe(const CFoo* var)
  {
    std::cout << "twe(tINT, const CFoo*)" << std::endl;
  }

int main(int argc, char* argv[])
  {
    CFoo foo;
    CBar bar;
    
    zug(foo);
    zug(bar);
    
    twe(&foo);
    twe(&bar);
  }
Compiles fine and outputs:
zug(tINT, const CFoo&)
CBar::foo()
twe(tINT, const CFoo*)
CBar::foo()
The problem here is that, while the specialized version of zug (and twe) is a match (ie: the code works fine without the unspecialized version of zug (and twe)), the unspecialized version is being preferred over the specialized version for subclasses of CFoo. I'm guessing this is the intended behavior. I'm just wondering if there is any way that I can specialize a template or function for a polymorphic type and its subclasses, and have this specialization preferred over the unspecialized function. Thanks for all help. G++ 4.4.0 Ubuntu Jaunty 64

Share this post


Link to post
Share on other sites
why do you need the unspecialized function anyway? if you have a templated function it should account for the CFoo type as well.

You override the templated function by implementing a duplicate function with no templates which takes precedence over the templated one, if you want the compiler to call the templated function instead then that makes the un-templated function totally useless as it will never be called (I think).

The only way the two functions could exist like this is if you renamed one of them. i.e.



template <typename tTYPE>
void zug(const tTYPE& var)
{
var.foo();
}

void zug2(const CFoo& var)
{
std::cout << "zug(tINT, const CFoo&)" << std::endl;
}

int main(int argc, char* argv[])
{
CFoo foo;
CBar bar;

zug(foo);
zug(bar);

}



Share this post


Link to post
Share on other sites
try the following


template <>
void zug(const CBar& var)
{
var.foo();
}



im going to guess that because one function is templated, and the other isnt, the compiler sees that your type matches the signature of the untemplated function.

the other solution would be to do the above, but provide the specialisation for the base type only.

eg:



template <typename tTYPE>
void zug(const tTYPE& var)
{
var.foo();
}

template <>
void zug(const CFoo& var)
{
std::cout << "zug(tINT, const CFoo&)" << std::endl;
}




this should let the compiler know to only use the specalised CFoo when the type is actually a CFoo

Share this post


Link to post
Share on other sites
Quote:
Why do you need the unspecialized function anyway?


It's kinda like an advanced version of std::ostream's << operator.

You can pass these categories of types:

1. A polymorphic formatting object.
2. A known type or set of types (eg pointer to anything).
3. An unknown type that has the correct traits (eg my_traits<type>::is_integer).
4. A completely unknown type (handled by a generic unknown-type method).

I need the unspecialized function so I can do #3, traits.
#1 is the base class for format-modifying objects.


Quote:
You override the templated function by implementing a duplicate function with no templates which takes precedence over the templated one
That's what I thought, too. But it isn't happening. The templated one is taking precedence even though both match (I tested this by removing the template one).


Quote:
try the following ... template <> ...

No effect sorry.

Even adding an arbitrary extra template parameter to make both functions templated doesn't help:

template <typename tANY, typename tTYPE>
void zug(tANY, const tTYPE& var)
{
var.foo();
}

template <typename tANY>
void zug(tANY, const CFoo& var)
{
std::cout << "zug(tINT, const CFoo&)" << std::endl;
}





The less-specialized function is still preferred over the more-specialized one.




Actually, I just had an idea about what is going on:

For bar to match the CFoo-typed parameter of zug() it needs to be cast from a CBar& to a CFoo&. Normally this is done implicitly, but it is still a cast.
So the less-specialized version is chosen over the more-specialized version simply because the more-specialized version needs a cast and the less-specialized version doesn't.

Still, I can't think of a way around it (except to make both versions require a cast, which may lead to ambiguity instead).

Share this post


Link to post
Share on other sites
You may want something like the following, note you could use boost for this and I have only done it for the function twe. Although this may not be what you are actually asking for.

#include <iostream>

template<typename B, typename T>
struct is_base
{
private:
typedef char (&true_)[1];
typedef char (&false_)[2];
static true_ test(B*);
static false_ test(...);
public:
enum { value = sizeof(test((T*)0)) == sizeof(true_) ? 1 : 0 };
};

class CFoo
{
public:
virtual void foo() const
{ std::cout << "CFoo::foo()" << std::endl; }
};

class CBar : public CFoo
{
public:
virtual void foo() const
{ std::cout << "CBar::foo()" << std::endl; }
};





template <typename tTYPE>
void zug(const tTYPE& var)
{
var.foo();
}

void zug(const CFoo& var)
{
std::cout << "zug(tINT, const CFoo&)" << std::endl;
}


template<bool is_derived>
struct twe_is_base;

template<>
struct twe_is_base<true>
{
template<typename T>
static void func(T* var)
{
std::cout << "twe(tINT, const CFoo&)" << std::endl;
}
};

template<>
struct twe_is_base<false>
{
template<typename T>
static void func(T* var)
{
var->foo();
}
};


template <typename tTYPE>
void twe(const tTYPE* var)
{
twe_is_base< is_base<CFoo,tTYPE>::value >::func(var);
}

int main()
{
CFoo foo;
CBar bar;

zug(foo);
zug(bar);

twe(&foo);
twe(&bar);
}


Share this post


Link to post
Share on other sites
Interesting.

I think you have the makings of an answer there, as long as the (...) doesn't take preference over the specialized type since (...) doesn't require a cast. If it does we're back to square one.

Another problem is that the return type of zug() differs. The polymorphic formatting objects return a temporary pointing at the interface, whereas everything else returns a reference to the interface itself.

But I can get around this by making a template return_of<type>::type that determines the return type.

I'll have a play and see whether I can get it working with your method.

Thanks.

Share this post


Link to post
Share on other sites
Success!
Thanks DMail.

Just for that I'll make sure I don't rate you up per your request :) JK.

Note: I used c++0x's decltype instead of sizeof. I guess boost will do this once c++0x becomes standard and supported.

#include <iostream>


class CFoo {};
class CBar : public CFoo {};

class CRyl {};
class CKyr {};


struct VTrue
{ static const bool s_value = true; };

struct VFalse
{ static const bool s_value = false; };


template <typename tTYPE, typename tBASE>
class is_derived_from
{
private:
static const VTrue test(tBASE* ptr);
static const VFalse test(...);
typedef decltype(test((tTYPE*)0)) vTEST;

public:
static const bool s_is = vTEST::s_value;
};


template <typename tT1, typename tT2, bool iSELECT> class TType;

template <typename tT1, typename tT2>
class TType<tT1, tT2, true>
{
public:
typedef tT1 vTYPE;
};

template <typename tT1, typename tT2>
class TType<tT1, tT2, false>
{
public:
typedef tT2 vTYPE;
};


template <typename tTYPE, bool iSELECT> class CGwy;

template <typename tTYPE>
class CGwy<tTYPE, true>
{
public:
static CRyl f(tTYPE& var)
{ std::cout << "zug(CFoo&)" << std::endl; }
};

template <typename tTYPE>
class CGwy<tTYPE, false>
{
public:
static CKyr f(tTYPE& var)
{ std::cout << "zug(tTYPE&)" << std::endl; }
};


template <typename tTYPE>
typename TType<CRyl, CKyr, is_derived_from<tTYPE, CFoo>::s_is>::vTYPE zug(tTYPE& var)
{
return CGwy<tTYPE, is_derived_from<tTYPE, CFoo>::s_is>::f(var);
}


int main(int argc, char* argv[])
{
CFoo foo;
CBar bar;
int i;

zug(foo);
zug(bar);
zug(i);

return 0;
}



Results in:
zug(CFoo&)
zug(CFoo&)
zug(tTYPE&)




The critical point of interest here is:

1: Non-variadic functions that require a cast seem to be preferred over variadic functions that do not require a cast.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this