We are currently studying classes in my Computing Science class. Today, the teacher introduced us to Switch Statements and I had the following question which he wasn't sure of:
Can switch statements be used alongside classes if we overload the == operator?
Exactly as you phrased the question: No. But it is possible to design a class that is compatible witch switch. This is an example using a barebones wrapper class for integers:
// Only possible in C++11 and newer.
class Integer
{
public:
constexpr explicit Integer(int p) : m_payload(p) {} // (1)
constexpr operator int() const { return m_payload; } // (2)
private:
int m_payload;
};
int main()
{
// For simplicity. This could be a user input or some other value
// determined when running the program.
Integer five(5);
switch (five) { // (4)
case Integer(5): // (3)
return 0;
default:
return 1;
}
}
I’m going to gloss over a lot of finer details. What the code does is this:
The value evaluated by the switch – in this case five in line (4) – must be either an integer-like value or implicitly convertible to an integer-like value. That’s what the operator int() (2) does – it’s called a conversion operator.
The cases of a switch must be constant integral expressions, i.e. they must must evaluate to an integer-like value and the evaluation must be possible at compile-time. To make line (3) work all of the following must be true:
An Integer object must be able to be created at compile-time. That is provided by the constexpr constructor in line (1). The constexpr is key here.
The object must be created with a value that’s actually known at compile time. That’s the literal 5 in line (3). You could not run your program, query the user for an integer and use that user input instead of the 5. The compiler has no way to anticipate the user’s input, after all.
An Integer object must be implicitly convertible to an integer-like value at compile time. That’s provided by the constexpr in like (2).
To sum up: Yes, you can design your own classes to be compatible with switch. But rather severe restrictions apply, and it has nothing to do with operator==().
Related
Think in a similar fashion like:
1. The bare name of an array is equivalent with the pointer to the first element, without the need to specify index 0.
2. toString() from Java makes it possible to use the name of an object as a string without calling any object method.
Now is there a way in C++ to use the name of a class object to refer to its first member?
Consider:
class Program
{
public:
int id;
char *str;
};
void function(int p)
{
//...
}
and then:
Program prog0;
function(prog0); // instead of function(prog0.id)
Any way to "hide" the member reference?
EDIT:
Why was the holyBlackCat's answer deleted? I was inclining to vote it as the best answer -- no offense, Mateusz. But he was the first to suggest conversion operator and the example was complete and simple.
In C++, such behaviour would be a cataclysm. If I understand correctly, Java tries to convert object of type A to object of type B by searching for first member in A, that is of type B or is implicitly convertible to B.
C++ wasn't designed that way. We like to write code, that is always predictable. You can achieve what you want, but for a price.
The best solution in this case would be conversion operator - consider:
class Program
{
public:
int id;
char *str;
operator int()
{
return this->id;
}
//You can have more than one!
operator const char*()
{
return this->str;
}
};
void function_int(int p)
{
}
void function_str(const char* s)
{
}
Now it is possible to do the following:
Program prog;
function_int(prog); //Equivalent of function_int(prog.id)
function_str(prog); //Equivalent of function_int(prog.str)
The price is, that if you add another int and place it before id it will not be used in conversion, because we stated in our operator explicitly, that "int content" of our class is represented by id and this member is considered when it comes to such conversion.
However, even this simple example shows some potential problems - overloading functions with integral and pointer types could result in very unpredictable behavior. When type contains conversion operators to both pointers and integers, it can get even worse.
Assume, that we have following function:
void func(unsigned long)
{
}
And we call func with argument of type Program. Which conversion operator would you expect to be called? Compiler knows how to convert Program to either int or const char*, but not unsigned long. This article on cppreference should help you to understand how implicit conversions work.
Also, as Barry pointed out, more meaningless constructs become available. Consider this one:
int x = prog + 2
What does it mean? It is perfectly valid code, though. That is why conversion operators should be dosed extremely carefully (in pre-C++11 era, there was a general advise, that every class should have at most one such operator).
Quote from MSDN:
If a conversion is required that causes an ambiguity, an error is generated. Ambiguities arise when more than one user-defined conversion is available or when a user-defined conversion and a built-in conversion exist.
Sometimes, simple solution to this problem is to mark conversion operator with explicit keyword, so you would need to change above calls to:
function_int((int)prog);
function_str((const char*)prog);
It is not as pretty as the previous form, but much safer. It basically means, that compiler is forbidden to perform any implicit conversion using operator marked as explicit. Very useful to avoid ambiguous calls, while still providing some flexibility in code - you can still very easily convert objects of one type to another, but you can be sure when and where these conversions are performed.
However, explicit conversion operators are still not supported by some compilers, as this is C++ 11 feature (for example, Visual C++ 11 doesn't support it).
You can read more about explicit keyword here.
Now is there a way in C++ to use the name of a class object to refer to its first member?
No, C++ doesn't have any reflection, so there's no way to actually determine what the "first member" is.
However, if what you really want is to get an ID for any object, you could just require that object to have that method:
template <typename T>
void function(const T& t) {
int id = t.getID();
// etc.
}
Without knowing more about your use-case, it's hard to know what to propose.
I need to convert the if else into switch statement, please help:
if (XMLString::compareString(xmlch_Title, XMLString::transcode("abc")) == 0 ) {
out_Config.abc = XMLString::transcode(xmlch_Value);
} else if (XMLString::compareString(xmlch_Title, XMLString::transcode("def")) == 0 ) {
out_Config.def = XMLString::transcode(xmlch_Value);
} .......
Note: Using a boolean expression as the condition of a switch would, in your case, be of little use since you then could only have two possible outcomes of the switch-statement:
Going to "case true: ", or "case false: "
Introduction
As stated in the Standard (n3797), the condition used with a switch must be implicitly convertiable to either integral, or enumeration, type.
6.4.2p2 The switch statement [stmt.switch]
The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type**. Integral promotions are performed. Any statement within the switch statement can be labeled with one or more case labels as follows:
case constant-expression:
where the constant-expression shall be a converted constant expression (5.19) of the promoted type of the switch condition. No two of the case constans in the same switch shall have the same value after conversion to the promoted type of the switch condition.
What this means is that the theoretical implementation below is ill-formed, since std::string can't implicitly be converted to an integral or enumeration type (1), and neither is it usable in a constant-expression (2).
std::string get_string ();
switch (get_string ()) { // (1)
case std::string ("stack"): ...; // <----.
case std::string ( "over"): ...; // <----|-- (2)
case std::string ( "flow"): ...; // <----'
}
Am I completely out of luck?
Not really, if you were to use a hashing function to generate a hash for the strings involved, you can theoretically use a switch; just be careful about potential collisions of said hashes.
Also note that there must be an implementation usable in constant-expressions for the case-labels.
Is it worth writing such complex code to solve this kind of problem? Probably not.
if you want to factor out the common part, you'll probably need to use an array of struct containing the transcoded string constant and a lambda doing the then operation. Something along the lines of:
#include <functional>
struct check_case {
const XMLString code;
const std::function <void (Config &, const XMLString &)> handler;
};
check_case check_list[2] = {
{XMLString::transcode("abc"), [](Config &out_Config, const XMLString &code) { out_Config.abc = code;}},
{XMLString::transcode("def"), [](Config &out_Config, const XMLString &code) { out_Config.def = code;}},
};
for (auto c : check_list) {
if (XMLString::compareString(xmlch_Title,c.code) == 0){
c.handler(out_Config, XMLString::transcode(xmlch_Value));
break;
}
}
It's still quite verbose though, and I'm not sure you'd find it more easily readable. Besides, you don't get to cascade your handlers as you could with a normal switch (which was perhaps the original reason for your question).
Alternatively, you could embed all that in a dedicated class, where it's easier to build a context (the class instance member fields). There it would be easier to cascade methods manually. As above, the readability isn't quite up to par either.
Look at it from a different point of view. Do it in two stages...
Stage 1: Extract the text in the quotes "abc" or "def"
Stage 2: switch (based on variable from stage 1) and case will provide implementation
This question requires knowledge of C++ template meta-programming as (indirectly) expression templates are involved. I say indirectly because its not directly a question on expression templates, but involves C++ type computations. If you don't know what that is please don't answer this question.
To avoid putting out a question without enough background information let me elaborate a bit on the general problem I am trying to solve and then go to the more specific parts.
Suppose you have a library that provides Integers that the user can do calculations with just like with ints.
Furthermore it is possible to construct a Integer from an int. Just like:
Integer<int> i(2);
Internally my Integer class is a class template:
template<class T>
class Integer {
// cut out
};
So I can define it on whatever integer type I like.
Now without changing the API, I would like to change the library in a way that if Integer was constructed from an int it should be represented internally by a different type, say IntegerLit. The reason for this is that I can speed up some calculation knowing that an instance of Integer was created from an int (can pass it as a int argument to a function instead of as a general object described by a base pointer + separate data. This just as a comment.)
It is essential that the type is different when constructing from an int because I need the compiler to take up different code paths depending on whether constructed from an int or not. I cannot do this with a runtime data flag. (The reason in short: The compiler generates a function that takes either an int or the above mentioned more general type of object depending on the type.)
Having this said I run into a problem: When the uses does something like this:
Integer<int> a,b(2);
a = b + b;
Here a should be the general Integer and b the specialized IntegerLit. However, my problem is how to express this in C++ as the user is free to use the very same type Integer to define her variables.
Making the types polymorphic, i.e. deriving IntegerLit from Integer won't work. It looks fine for a moment. However since the user creates instances of Integer (the base class) that won't work because it is the base class the compiler sticks into the expression tree (this is why expression templates are involved in the question). So again no distinction is possible between the two cases. Doing a RTTI check a la dynamic cast is really not what I want on that point.
More promising seems to be adding a literal template parameter bool lit to the type saying if it was constructed from an int or not. The point is to not specify the conversion rule for one not literal one but do specify it for the other case.
However, I can't get that to work. The following code only compiles (GCC 4.7 C++11) if not constructing from an int. Otherwise it fails because the Integer is not specified with true as the value for lit. So the compiler searches the default implementation which doesn't have the conversion rule. It is not an option changing the API and requiring to write Integer<int,true> when constructing from an int.
template<class T,bool lit=false>
class Integer
{
public:
Integer() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
T F;
};
template<>
template<class T>
class Integer<T,true>
{
public:
Integer(int i) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
T F;
};
I am starting wondering if something like this is possible with C++.
Is there maybe a new feature of C++11 that can help here?
No, this isn't how C++ works. If you define b as Integer b, then it's an Integer. THis applies regardless of the expression which is subsequently used to initialize b.
Also, consider the following: extern Integer b;. There's an expression somewhere else that initializes b, yet the compiler still has to figure out here what type b has. (Not "will have", but "has").
You can't do that, exactly anyways.
But using "auto" you can come close.
// The default case
Integer<int> MakeInt()
{
return Integer<int>();
}
// The generic case
template <class T>
Integer<T> MakeInt(T n)
{
return Integer<T>(n);
}
// And specific to "int"
Integer<IntegerLit> MakeInt(int n)
{
return Integer<IntegerLit>(n);
}
auto a = MakeInt();
auto b = MakeInt(2);
auto c = MakeInt('c');
auto d = MakeInt(2.5);
You can't do that. Once you've said a variable is Integer<int> that is the variable's type. What you can do is make the underlying rep of the Integer vary depending on which constructor is used, something like this:
template<class T>
class Integer {
// cut out
Integer() : rep_(IntRep()) {}
explicit Integer(int val) : rep_(IntLitRep(val)) {}
private:
boost::variant<IntRep, IntLitRep> rep_;
};
Then you can easily determine which variant version is active and utilize different code paths when needed.
EDIT: In this case even though the type of the Integer is the same, you can easily use template functions to make it appear that it's behaving as two separate types (since the rep changes effective type).
I understand that the keyword explicit can be used to prevent implicit conversion.
For example
Foo {
public:
explicit Foo(int i) {}
}
My question is, under what condition, implicit conversion should be prohibited? Why implicit conversion is harmful?
Use explicit when you would prefer a compiling error.
explicit is only applicable when there is one parameter in your constructor (or many where the first is the only one without a default value).
You would want to use the explicit keyword anytime that the programmer may construct an object by mistake, thinking it may do something it is not actually doing.
Here's an example:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
With the following code you are allowed to do this:
int age = 29;
//...
//Lots of code
//...
//Pretend at this point the programmer forgot the type of x and thought string
str s = x;
But the caller probably meant to store "3" inside the MyString variable and not 3. It is better to get a compiling error so the user can call itoa or some other conversion function on the x variable first.
The new code that will produce a compiling error for the above code:
class MyString
{
public:
explicit MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
Compiling errors are always better than bugs because they are immediately visible for you to correct.
It introduces unexpected temporaries:
struct Bar
{
Bar(); // default constructor
Bar( int ); // value constructor with implicit conversion
};
void func( const Bar& );
Bar b;
b = 1; // expands to b.operator=( Bar( 1 ));
func( 10 ); // expands to func( Bar( 10 ));
A real world example:
class VersionNumber
{
public:
VersionNumber(int major, int minor, int patch = 0, char letter = '\0') : mMajor(major), mMinor(minor), mPatch(patch), mLetter(letter) {}
explicit VersionNumber(uint32 encoded_version) { memcpy(&mLetter, &encoded_version, 4); }
uint32 Encode() const { int ret; memcpy(&ret, &mLetter, 4); return ret; }
protected:
char mLetter;
uint8 mPatch;
uint8 mMinor;
uint8 mMajor;
};
VersionNumber v = 10; would almost certainly be an error, so the explicit keyword requires the programmer to type VersionNumber v(10); and - if he or she is using a decent IDE - they will notice through the IntelliSense popup that it wants an encoded_version.
Mostly implicit conversion is a problem when it allows code to compile (and probably do something strange) in a situation where you did something you didn't intend, and would rather the code didn't compile, but instead some conversion allows the code to compile and do something strange.
For example, iostreams have a conversion to void *. If you're bit tired and type in something like: std::cout << std::cout; it will actually compile -- and produce some worthless result -- typically something like an 8 or 16 digit hexadecimal number (8 digits on a 32-bit system, 16 digits on a 64-bit system).
At the same time, I feel obliged to point out that a lot of people seem to have gotten an almost reflexive aversion to implicit conversions of any kind. There are classes for which implicit conversions make sense. A proxy class, for example, allows conversion to one other specific type. Conversion to that type is never unexpected for a proxy, because it's just a proxy -- i.e. it's something you can (and should) think of as completely equivalent to the type for which it's a proxy -- except of course that to do any good, it has to implement some special behavior for some sort of specific situation.
For example, years ago I wrote a bounded<T> class that represents an (integer) type that always remains within a specified range. Other that refusing to be assigned a value outside the specified range, it acts exactly like the underlying intger type. It does that (largely) by providing an implicit conversion to int. Just about anything you do with it, it'll act like an int. Essentially the only exception is when you assign a value to it -- then it'll throw an exception if the value is out of range.
It's not harmful for the experienced. May be harmful for beginner or a fresher debugging other's code.
"Harmful" is a strong statement. "Not something to be used without thought" is a good one. Much of C++ is that way (though some could argue some parts of C++ are harmful...)
Anyway, the worst part of implicit conversion is that not only can it happen when you don't expect it, but unless I'm mistaken, it can chain... as long as an implicit conversion path exists between type Foo and type Bar, the compiler will find it, and convert along that path - which may have many side effects that you didn't expect.
If the only thing it gains you is not having to type a few characters, it's just not worth it. Being explicit means you know what is actually happening and won't get bit.
To expand Brian's answer, consider you have this:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
// ...
};
This actually allows this code to compile:
MyString mystr;
// ...
if (mystr == 5)
// ... do something
The compiler doesn't have an operator== to compare MyString to an int, but it knows how to make a MyString out of an int, so it looks at the if statement like this:
if (mystr == MyString(5))
That's very misleading since it looks like it's comparing the string to a number. In fact this type of comparison is probably never useful, assuming the MyString(int) constructor creates an empty string. If you mark the constructor as explicit, this type of conversion is disabled. So be careful with implicit conversions - be aware of all the types of statements that it will allow.
I use explicit as my default choice for converting (single parameter or equivalent) constructors. I'd rather have the compiler tell me immediately when I'm converting between one class and another and make the decision at that point if the conversion is appropriate or instead change my design or implementation to remove the need for the conversion completely.
Harmful is a slightly strong word for implicit conversions. It's harmful not so much for the initial implementation, but for maintenance of applications. Implicit conversions allow the compiler to silently change types, especially in parameters to yet another function call - for example automatically converting an int into some other object type. If you accidentally pass an int into that parameter the compiler will "helpfully" silently create the temporary for you, leaving you perplexed when things don't work right. Sure we can all say "oh, I'll never make that mistake", but it only takes one time debugging for hours before one starts thinking maybe having the compiler tell you about those conversions is a good idea.
I'm attempting to map a set of key presses to a set of commands. Because I process the commands from several places, I'd like to set up a layer of abstraction between the keys and the commands so that if I change the underlying key mappings, I don't have to change very much code. My current attempt looks like this:
// input.h
enum LOGICAL_KEYS {
DO_SOMETHING_KEY,
DO_SOMETHING_ELSE_KEY,
...
countof_LOGICAL_KEYS
};
static const SDLKey LogicalMappings[countof_LOGICAL_KEYS] = {
SDLK_RETURN, // Do Something
SDLK_ESCAPE, // Do Something Else
...
};
// some_other_file.cpp
...
switch( event.key.keysym.key ) {
case LogicalMappings[ DO_SOMETHING_KEY ]:
doSomething();
break;
case LogicalMappings[ DO_SOMETHING_ELSE_KEY ]:
doSomethingElse();
break;
...
}
When I try to compile this (gcc 4.3.2) I get the error message:
error: 'LogicalMappings' cannot appear in a constant-expression
I don't see why the compiler has a problem with this. I understand why you're not allowed to have variables in a case statement, but I was under the impression that you could use constants, as they could be evaluated at compile-time. Do constant arrays not work with switch statements? If so, I suppose I could just replace the array with something like:
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING = SDLK_RETURN;
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING_ELSE = SDLK_ESCAPE;
...
But that seems much less elegant. Does anybody know why you can't use a constant array here?
EDIT: I've seen the bit of the C++ standard that claims that, "An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5)...". I still don't see why a constant array doesn't count as an "enumeration type initialized with a constant expression." It could just be that the answer to my question is "because that's the way that it is," and I'll have to work around it. But if that's the case, it's sort of disappointing, because the compiler really could determine those values at compile-time.
Referring to sections of the C++ standard: 6.4.2 requires that case expressions evaluate to an integral or enumeration constant. 5.19 defines what that is:
An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
So if your question was "why does the compiler reject this", one answer is "because the standard says so".
Array references aren't "constant enough", regardless.
You just need to do the mapping slightly differently. You want the same action to occur when the logical key is pressed, so use the logical key codes in the case clauses of the switch statement. Then map the actual key code to the logical code, possibly in the switch itself, or possibly before-hand. You can still use the LogicalMappings array, or a similar construct. And, as an aid to G11N (globalization), you can even make the mapping array non-constant so that different people can remap the keys to suit their needs.
I'll go on a limb here since nobody else replied to this and I've been mostly doing Java recently, not C++, but as far as I seem to recall an array lookup is not considered a constant integer even if the result of the lookup can be determined at compile time. This may even be an issue in the syntax.
Is there a comparison operator defined for the "LogicalMappings"? If not then that is the error.
There is a library called signal in boost that will help you create a event mapping abstraction.If you have time this should be better approach
you could also use an array of function pointers or functors (I suppose functor addresses), to avoid the switch statement altogether & just go from array index -> function pointer / functors directly.
for example (warning, untested code follows)
class Event // you probably have this defined already
{
}
class EventHandler // abstract base class
{
public:
virtual void operator()(Event& e) = 0;
};
class EventHandler1
{
virtual void operator()(Event& e){
// do something here
}
};
class EventHandler2
{
virtual void operator()(Event& e){
// do something here
}
};
EventHandler1 ev1;
EventHandler2 ev2;
EventHandler *LogicalMappings[countof_LOGICAL_KEYS] = {
&ev1,
&ev2,
// more here...
};
// time to use code:
Event event;
if (event.key.keysym.key < countof_LOGICAL_KEYS)
{
EventHandler *p = LogicalMappings[event.key.keysym.key];
if (p != NULL)
(*p)(event);
}
A compiler guru at work explained this to me. The problem is that the array itself is constant, but indices to it aren't necessarily const. Thus, the expression LogicalMappings[some_variable] couldn't be evaluated at compile-time, so the array winds up being stored in memory anyway rather than getting compiled out. There's still no reason why the compiler couldn't statically evaluate array references with a const or literal index, so what I want to do should theoretically be possible, but it's a bit trickier than I'd thought, so I can understand why gcc doesn't do it.