Question

Kind of a random question...

What I'm looking for is a way to express a cast operation which uses a defined operator of the class instance I'm casting from, and generates a compile-time error if there is not a defined cast operator for the type. So, for example, what I'm looking for is something like:

template< typename RESULT_TYPE, typename INPUT_TYPE >
RESULT_TYPE operator_cast( const INPUT_TYPE& tValue )
{
    return tValue.operator RESULT_TYPE();
}

// Should work...
CString sString;
LPCTSTR pcszString = operator_cast< LPCTSTR >( sString );

// Should fail...
int iValue = 42;
DWORD dwValue = operator_cast< DWORD >( iValue );

Interesting side-note: The above code crashes the VS2005 C++ compiler, and doesn't compile correctly in the VS2008 C++ compiler due to what I'm guessing is a compiler bug, but hopefully demonstrates the idea.

Anybody know of any way to achieve this effect?

Edit: More rationale, to explain why you might use this. Say you have a wrapper class which is supposed to encapsulate or abstract a type, and you're casting it to the encapsulated type. You could use static_cast<>, but that might work when you wanted it to fail (ie: the compiler chooses an operator which is allowed to convert to the type you asked for, when you wanted a failure because that operator is not present).

Admittedly it's an uncommon case, but it's annoying that I can't express exactly what I want the compiler to do in an encapsulated function... hence the question here.

Was it helpful?

Solution

The code you posted works with the Cameau compiler (which is usually a good indication that it's valid C++).

As you know a valid cast consists of no more than one user defined cast, so a possible solution I was thinking of was adding another user defined cast by defining a new type in the cast template and having a static assert that no cast is available from the new type to the result type (using boost is_convertible), however this doesn't distinguish between cast operators and cast constructors (ctor with one argument) and alows additional casts to take place (e.g. void* to bool). I'm not sure if making a distinction between cast operators and cast constructors is the the correct thing to do but that's what the question states.

After a couple of days mulling this over it hit me, you can simply take the address of the cast operator. This is slightly easier said than done due to C++'s hairy pointer to member syntax (it took me way longer than expected to get it right). I don't know if this works on VS2008, I only checked it on Cameau.

template< typename Res, typename T>
Res operator_cast( const T& t )
{
    typedef Res (T::*cast_op_t)() const;
    cast_op_t cast_op = &T::operator Res;
    return (t.*cast_op)();
}

Edit: I got a chance to test it on VS2005 and VS2008. My findings differ from the original poster's.

  • On VS2008 the original version seems to work fine (as does mine).
  • On VS2005 the original version only crashes the compiler when casting from a built in type (e.g. casting int to int) after providing a compilation error which doesn't seem so bad too me and my version seems to works in all cases.

OTHER TIPS

Using a converting constructor marked explicit is how you would prevent the compiler from allowing implicitly converted types from initializing your wrapper class.

As template-related compiler error messages are usually a complete pain to unravel, if you don't mind specifying each conversion you can get the compiler to emit a more instructive message in the fail case by providing a default template definition too. This uses the fact that the compiler will only attempt to compile code in templates that is actually invoked.

#include <string>

// Class to trigger compiler warning   
class NO_OPERATOR_CONVERSION_AVAILABLE
{
private:
   NO_OPERATOR_CONVERSION_AVAILABLE(){};
};

// Default template definition to cause compiler error
template<typename T1, typename T2> T1 operator_cast(const T2&)
{
   NO_OPERATOR_CONVERSION_AVAILABLE a;
   return T1();
}

// Template specialisation
template<> std::string operator_cast(const std::string &x)
{
   return x;
}

sounds like you want template specialization, something like this would do:

/* general template */
template<typename T1, typename T2> T1 operator_cast(const T2 &x);

/* do this for each valid cast */
template<> LPCTSTR operator_cast(const CString &x) { return (LPCTSTR)x; }

EDIT: As noted in another post, you can put something in the general version to give you a more useful error message if an unsupported cast is performed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top