By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,730 Members | 1,156 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,730 IT Pros & Developers. It's quick & easy.

template conversion operator clarification needed

P: n/a
Hello,

I have come back to C++ after a couple of years with Java so I am quite
rusty and this question may seem poor:

My platform is Windows XP with MSVC 7.1.

I have a class with a templatized conversion operator defined as
follows:

----------------------------------------------------------
template<class T>
class IBitImpl : public virtual IBit<T>
{
std::numeric_limits<T> m_limits;
T m_bit;
// other fns/ctor's etc

template<class C> operator C() const
throw(InvalidCastException)
{
if(std::numeric_limits<C>::digits < m_limits.digits)
{
throw InvalidCastException(L"Invalid argument -
Destination type C cannot accomodate source type T. ");
}
return (C) m_bit;
}
};
----------------------------------------------------------

So, I up and write a simple test:

----------------------------------------------------------
int x = 121234234;
IBitImpl<int> lowbit2(2);
bool isset = (1 == (x & lowbit2));
std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set?
"<<std::boolalpha<<isset<<std::endl;
----------------------------------------------------------

I get an error indicating that there is an ambiguity because in (x &
lowbit2) the conversion can be any one of (long, bool, unsigned long,
etc).

I thought that the compiler looks at x and upcasts lowbit2 to the
appropriate type (in this case int). This doesn't seem to be the case
here.

I figured the whole problem ws because of the conversion operator
defined above. Consequently, I changed the expression to make the case
explicit as follows:

---------------------------------------------------------
bool isset = (1 == (x & (int) lowbit2));
---------------------------------------------------------

which needless to say, worked fine. However, I don't understand why the
correct cast isn't automatically applied. I am missing something here
and would appreciate it if someone would point me in the right
direction.

thanks,

-vijai.

Aug 6 '05 #1
Share this Question
Share on Google+
5 Replies


P: n/a
My stupidity. The code snippet for the expression should be

bool isset = ( (x & (int) lowbit2));

However, that doesn't detract from my original question.

-vijai.

Aug 7 '05 #2

P: n/a
* Vijai Kalyan:

I have come back to C++ after a couple of years with Java
Noted.

so I am quite
rusty and this question may seem poor:

My platform is Windows XP with MSVC 7.1.
Irrelevant.

I have a class with a templatized conversion operator defined as
follows:

----------------------------------------------------------
template<class T>
class IBitImpl : public virtual IBit<T>
Naming: "Bit" would be a good name for this class.
Performance/design: difficult to see any advantage in having IBit<T>.
{
std::numeric_limits<T> m_limits;
Should be a 'typedef' (if anything), not a data member.

T m_bit;
Naming: is this the bit number, let's call it n, or is it the bit
value 1<<n?

Only for the latter case does it make sense to use type T.

In the test program it seems there is a member function that computes n from
m_bit, which is a bit awkward, opposite of the simplest & most efficient.

// other fns/ctor's etc

template<class C> operator C() const
throw(InvalidCastException)
Javaism : avoid throw specifications in C++, except 'throw()'.
Design : don't be afraid to use 'assert'.
Types : if possible use standard exception classes or classes derived from
them, e.g. here, std::bad_cast (but: rather 'assert' instead).
{
if(std::numeric_limits<C>::digits < m_limits.digits)
See above regarding 'm_limits'.

{
throw InvalidCastException(L"Invalid argument -
Destination type C cannot accomodate source type T. ");
See above regarding exception types and use of 'assert'.

}
return (C) m_bit;
Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.

See above regarding naming and resulting confusion about what 'm_bit' is.
}
};
----------------------------------------------------------

So, I up and write a simple test:

----------------------------------------------------------
int x = 121234234;
IBitImpl<int> lowbit2(2);
bool isset = (1 == (x & lowbit2));
std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set?
"<<std::boolalpha<<isset<<std::endl;
----------------------------------------------------------

I get an error indicating that there is an ambiguity because in (x &
lowbit2) the conversion can be any one of (long, bool, unsigned long,
etc).
Yes. Built-in operators do not constrain the types of their arguments,
except to the set of types the operator in question is defined for.
However, if you define your own '&' operator you can do that, e.g.

template< typename T, typename U >
T operator&( T a, Bit<U> const& b ){ return a & static_cast<T>( b ); }

template< typename T, typename U >
U operator&( Bit<T> const& a, U b ){ return static_cast<U>( a ) & b; }

I thought that the compiler looks at x and upcasts lowbit2 to the
'upcast' is something else entirely; here you're talking about a conversion,
in no particular direction.

appropriate type (in this case int). This doesn't seem to be the case
here.

I figured the whole problem ws because of the conversion operator
defined above. Consequently, I changed the expression to make the case
explicit as follows:

---------------------------------------------------------
bool isset = (1 == (x & (int) lowbit2));
---------------------------------------------------------

which needless to say, worked fine. However, I don't understand why the
correct cast isn't automatically applied.
There is no correct conversion because there is no constraint whatsoever
(except to the set of types that the built-in '&' is defined for).

I am missing something here
and would appreciate it if someone would point me in the right
direction.


See above.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Aug 7 '05 #3

P: n/a
> > ----------------------------------------------------------
template<class T>
class IBitImpl : public virtual IBit<T>
Naming: "Bit" would be a good name for this class.
Performance/design: difficult to see any advantage in having IBit<T>.


Well, IBit is really the interface that defines the requirements for
IBitImpl.

template<class T>
class IBit
{
public:
template<class C> IBit<T>& operator=(C);
template<class C> IBit<T>& operator=(const IBit<C>&);
template<class C> operator C() const;

virtual inline T Bit() const = 0;
virtual inline T Mask() const = 0;
};
{
std::numeric_limits<T> m_limits;
Your are right. A member is not called for.
Should be a 'typedef' (if anything), not a data member.

T m_bit;
Naming: is this the bit number, let's call it n, or is it the bit
value 1<<n?


m_bit is the bit number. There is a data member m_mask that stores the
mask.
Only for the latter case does it make sense to use type T.
Here, I don't know. I presume an 'int' or 'short' can equally hold all
possible bit numbers.
In the test program it seems there is a member function that computes n from
m_bit, which is a bit awkward, opposite of the simplest & most efficient.
No. The mask is computed when the object is created and stored in
m_mask of type T.
// other fns/ctor's etc

template<class C> operator C() const
throw(InvalidCastException)


Javaism : avoid throw specifications in C++, except 'throw()'.
Design : don't be afraid to use 'assert'.
Types : if possible use standard exception classes or classes derived from
them, e.g. here, std::bad_cast (but: rather 'assert' instead).


True. I should have used bad_cast. On the other hand, most of the
standard exception classes don't seem to have a wide-string variant of
their constructors. I would rather not mix the two and prefer to use
the wide-string variant where possible. Of course, that is irrelevant
strictly speaking. I can derive form bad_cast and provide a
wide-string constructor.
{
if(std::numeric_limits<C>::digits < m_limits.digits)


See above regarding 'm_limits'.

{
throw InvalidCastException(L"Invalid argument -
Destination type C cannot accomodate source type T. ");


See above regarding exception types and use of 'assert'.

}
return (C) m_bit;


Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.


My mistake.
See above regarding naming and resulting confusion about what 'm_bit' is.


As I said, m_bit is the bit number. m_mask is the actual mask. So,

m_mask = (1 << m_bit);
}
};
----------------------------------------------------------

So, I up and write a simple test:

----------------------------------------------------------
int x = 121234234;
IBitImpl<int> lowbit2(2);
bool isset = (1 == (x & lowbit2));
std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set?
"<<std::boolalpha<<isset<<std::endl;
----------------------------------------------------------

I get an error indicating that there is an ambiguity because in (x &
lowbit2) the conversion can be any one of (long, bool, unsigned long,
etc).


Yes. Built-in operators do not constrain the types of their arguments,
except to the set of types the operator in question is defined for.
However, if you define your own '&' operator you can do that, e.g.

template< typename T, typename U >
T operator&( T a, Bit<U> const& b ){ return a & static_cast<T>( b ); }

template< typename T, typename U >
U operator&( Bit<T> const& a, U b ){ return static_cast<U>( a ) & b; }

I thought that the compiler looks at x and upcasts lowbit2 to the


'upcast' is something else entirely; here you're talking about a conversion,
in no particular direction.


Ah, I see. Ok. Well, I thought about defining those operators and then
started wondering why the conversion operator wouldn't work in general.
Of course, that means I would have to define a | operator as well.
appropriate type (in this case int). This doesn't seem to be the case
here.

I figured the whole problem ws because of the conversion operator
defined above. Consequently, I changed the expression to make the case
explicit as follows:

---------------------------------------------------------
bool isset = (1 == (x & (int) lowbit2));
---------------------------------------------------------

which needless to say, worked fine. However, I don't understand why the
correct cast isn't automatically applied.


There is no correct conversion because there is no constraint whatsoever
(except to the set of types that the built-in '&' is defined for).

I am missing something here
and would appreciate it if someone would point me in the right
direction.


See above.


Great! Thanks a lot for the clarifications.

-vijai.

Aug 7 '05 #4

P: n/a
* Vijai Kalyan:
}
return (C) m_bit;


Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.


My mistake.
See above regarding naming and resulting confusion about what 'm_bit' is.


As I said, m_bit is the bit number. m_mask is the actual mask. So,

m_mask = (1 << m_bit);


In that case, in the test program's

bool isset = (1 == (x & lowbit2));


the sub-expression (I've added the necessary static_cast)

x & static_cast<int>( lowbit2 )

will not give correct result, because x isn't and'ed with a mask but with a
bitnumber.

This particular bug would probably have been avoided with more descriptive
naming... ;-)

There's also another bug in that expression, namely, the comparision with 1.

To avoid silly warnings from the compiler you can

A) compare the '&' result with the mask, or
B) compare the '&' result with 0, or
C) apply the negation operator '!' twice to the '&' result.

To avoid such bugs in general, try to design classes where it requires great
effort to use them incorrectly -- e.g., take control of all relevant ops.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Aug 7 '05 #5

P: n/a
As a matter of fact, it is kind of funny.

The conversion operator actually returns the "mask" instead of the
"bit". So,

(x & lowbit2)

would work.

But that is not really why I replied.

Considering that in the above expression, an explicit cast is required
to use the mask writting the correct expression as:

x & (int) lowbit2

really enforces a certain amount of safety primarily because the
conversion operator applied checks to make sure that the destination
type (in this case int) can actually hold the mask. I would think that
is actually a "good side-effect" of the conversion operator although I
am not too sure.

For example, the following would most definitely throw a runtime
exception "bad_cast"

char x = 'a';
x & (char) lowbit2.

Likewise, so would the following:

IBitImpl<int> lowbiti(2);
IBitImpl<char> lowbitc = lowbiti;

This of course not being due to the conversion operator but due to the
templatized constructor:

template<typename C> IBitImpl(const IBitImpl<C>&)

which also performs the check that C can hold T.

However, the following wouldn't:

IBitImpl<char> lowbitc(2);
IBitImpl<int> lowbiti = lowbitc;

since an integer mask can certainly accomodate a char mask.

Interesting no?

-vijai.

Aug 8 '05 #6

This discussion thread is closed

Replies have been disabled for this discussion.