Reputation: 6846
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.
Upvotes: 3
Views: 883
Reputation: 114725
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.
Upvotes: 3
Reputation: 487
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;
}
Upvotes: 1
Reputation: 90442
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.
Upvotes: 0
Reputation: 36439
Using a converting constructor marked explicit is how you would prevent the compiler from allowing implicitly converted types from initializing your wrapper class.
Upvotes: 1