Related
I read few questions here on SO about this topic which seems yet confusing to me. I've just begun to learn C++ and I haven't studied templates yet or operator overloading and such.
Now is there a simple way to overload
class My {
public:
int get(int);
char get(int);
}
without templates or strange behavior? or should I just
class My {
public:
int get_int(int);
char get_char(int);
}
?
No there isn't. You can't overload methods based on return type.
Overload resolution takes into account the function signature. A function signature is made up of:
function name
cv-qualifiers
parameter types
And here's the quote:
1.3.11 signature
the information about a function that participates in overload
resolution (13.3): its parameter-type-list (8.3.5) and, if the
function is a class member, the cv-qualifiers (if any) on the function
itself and the class in which the member function is declared. [...]
Options:
1) change the method name:
class My {
public:
int getInt(int);
char getChar(int);
};
2) out parameter:
class My {
public:
void get(int, int&);
void get(int, char&);
}
3) templates... overkill in this case.
It's possible, but I'm not sure that it's a technique I'd recommend for
beginners. As in other cases, when you want the choice of functions to
depend on how the return value is used, you use a proxy; first define
functions like getChar and getInt, then a generic get() which
returns a Proxy like this:
class Proxy
{
My const* myOwner;
public:
Proxy( My const* owner ) : myOwner( owner ) {}
operator int() const
{
return myOwner->getInt();
}
operator char() const
{
return myOwner->getChar();
}
};
Extend it to as many types as you need.
As stated before, templates are overkill in this case, but it is still an option worth mentioning.
class My {
public:
template<typename T> T get(int);
};
template<> int My::get<int>(int);
template<> char My::get<char>(int);
No, you can't overload by return type; only by parameter types, and const/volatile qualifiers.
One alternative would be to "return" using a reference argument:
void get(int, int&);
void get(int, char&);
although I would probably either use a template, or differently-named functions like your second example.
Resurrecting an old thread, but I can see that nobody mentioned overloading by ref-qualifiers. Ref-qualifiers are a language feature added in C++11 and I only recently stumbled upon it - it's not so widespread as e.g. cv-qualifiers. The main idea is to distinguish between the two cases: when the member function is called on an rvalue object, and when is called on an lvalue object. You can basically write something like this (I am slightly modifying OP's code):
#include <stdio.h>
class My {
public:
int get(int) & { // notice &
printf("returning int..\n");
return 42;
}
char get(int) && { // notice &&
printf("returning char..\n");
return 'x';
};
};
int main() {
My oh_my;
oh_my.get(13); // 'oh_my' is an lvalue
My().get(13); // 'My()' is a temporary, i.e. an rvalue
}
This code will produce the following output:
returning int..
returning char..
Of course, as is the case with cv-qualifiers, both function could have returned the same type and overloading would still be successful.
You can think this way:
You have:
int get(int);
char get(int);
And, it is not mandatory to collect the return value of the function while invoking.
Now, You invoke
get(10); -> there is an ambiguity here which function to invoke.
So, No meaning if overloading is allowed based on the return type.
While most of the other comments on this problem are technically correct, you can effectively overload the return value if you combine it with overloading input parameter. For example:
class My {
public:
int get(int);
char get(unsigned int);
};
DEMO:
#include <stdio.h>
class My {
public:
int get( int x) { return 'I'; };
char get(unsinged int x) { return 'C'; };
};
int main() {
int i;
My test;
printf( "%c\n", test.get( i) );
printf( "%c\n", test.get((unsigned int) i) );
}
The resulting out of this is:
I
C
There is no way to overload by return type in C++. Without using templates, using get_int and get_char will be the best you can do.
You can't overload methods based on return types. Your best bet is to create two functions with slightly different syntax, such as in your second code snippet.
you can't overload a function based on the return type of the function.
you can overlead based on the type and number of arguments that this function takes.
I used James Kanze's answer using a proxy:
https://stackoverflow.com/a/9569120/262458
I wanted to avoid using lots of ugly static_casts on a void*, so I did this:
#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>
struct JoyDev {
private:
union {
SDL_GameController* dev_gc = nullptr;
SDL_Joystick* dev_js;
};
public:
operator SDL_GameController*&() { return dev_gc; }
operator SDL_Joystick*&() { return dev_js; }
SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
SDL_Joystick*& operator=(SDL_Joystick* p) { dev_js = p; return dev_js; }
};
struct JoyState {
public:
JoyDev dev;
};
int main(int argc, char** argv)
{
JoyState js;
js.dev = SDL_JoystickOpen(0);
js.dev = SDL_GameControllerOpen(0);
SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);
return 0;
}
Works perfectly!
I read few questions here on SO about this topic which seems yet confusing to me. I've just begun to learn C++ and I haven't studied templates yet or operator overloading and such.
Now is there a simple way to overload
class My {
public:
int get(int);
char get(int);
}
without templates or strange behavior? or should I just
class My {
public:
int get_int(int);
char get_char(int);
}
?
No there isn't. You can't overload methods based on return type.
Overload resolution takes into account the function signature. A function signature is made up of:
function name
cv-qualifiers
parameter types
And here's the quote:
1.3.11 signature
the information about a function that participates in overload
resolution (13.3): its parameter-type-list (8.3.5) and, if the
function is a class member, the cv-qualifiers (if any) on the function
itself and the class in which the member function is declared. [...]
Options:
1) change the method name:
class My {
public:
int getInt(int);
char getChar(int);
};
2) out parameter:
class My {
public:
void get(int, int&);
void get(int, char&);
}
3) templates... overkill in this case.
It's possible, but I'm not sure that it's a technique I'd recommend for
beginners. As in other cases, when you want the choice of functions to
depend on how the return value is used, you use a proxy; first define
functions like getChar and getInt, then a generic get() which
returns a Proxy like this:
class Proxy
{
My const* myOwner;
public:
Proxy( My const* owner ) : myOwner( owner ) {}
operator int() const
{
return myOwner->getInt();
}
operator char() const
{
return myOwner->getChar();
}
};
Extend it to as many types as you need.
As stated before, templates are overkill in this case, but it is still an option worth mentioning.
class My {
public:
template<typename T> T get(int);
};
template<> int My::get<int>(int);
template<> char My::get<char>(int);
No, you can't overload by return type; only by parameter types, and const/volatile qualifiers.
One alternative would be to "return" using a reference argument:
void get(int, int&);
void get(int, char&);
although I would probably either use a template, or differently-named functions like your second example.
Resurrecting an old thread, but I can see that nobody mentioned overloading by ref-qualifiers. Ref-qualifiers are a language feature added in C++11 and I only recently stumbled upon it - it's not so widespread as e.g. cv-qualifiers. The main idea is to distinguish between the two cases: when the member function is called on an rvalue object, and when is called on an lvalue object. You can basically write something like this (I am slightly modifying OP's code):
#include <stdio.h>
class My {
public:
int get(int) & { // notice &
printf("returning int..\n");
return 42;
}
char get(int) && { // notice &&
printf("returning char..\n");
return 'x';
};
};
int main() {
My oh_my;
oh_my.get(13); // 'oh_my' is an lvalue
My().get(13); // 'My()' is a temporary, i.e. an rvalue
}
This code will produce the following output:
returning int..
returning char..
Of course, as is the case with cv-qualifiers, both function could have returned the same type and overloading would still be successful.
You can think this way:
You have:
int get(int);
char get(int);
And, it is not mandatory to collect the return value of the function while invoking.
Now, You invoke
get(10); -> there is an ambiguity here which function to invoke.
So, No meaning if overloading is allowed based on the return type.
While most of the other comments on this problem are technically correct, you can effectively overload the return value if you combine it with overloading input parameter. For example:
class My {
public:
int get(int);
char get(unsigned int);
};
DEMO:
#include <stdio.h>
class My {
public:
int get( int x) { return 'I'; };
char get(unsinged int x) { return 'C'; };
};
int main() {
int i;
My test;
printf( "%c\n", test.get( i) );
printf( "%c\n", test.get((unsigned int) i) );
}
The resulting out of this is:
I
C
There is no way to overload by return type in C++. Without using templates, using get_int and get_char will be the best you can do.
You can't overload methods based on return types. Your best bet is to create two functions with slightly different syntax, such as in your second code snippet.
you can't overload a function based on the return type of the function.
you can overlead based on the type and number of arguments that this function takes.
I used James Kanze's answer using a proxy:
https://stackoverflow.com/a/9569120/262458
I wanted to avoid using lots of ugly static_casts on a void*, so I did this:
#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>
struct JoyDev {
private:
union {
SDL_GameController* dev_gc = nullptr;
SDL_Joystick* dev_js;
};
public:
operator SDL_GameController*&() { return dev_gc; }
operator SDL_Joystick*&() { return dev_js; }
SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
SDL_Joystick*& operator=(SDL_Joystick* p) { dev_js = p; return dev_js; }
};
struct JoyState {
public:
JoyDev dev;
};
int main(int argc, char** argv)
{
JoyState js;
js.dev = SDL_JoystickOpen(0);
js.dev = SDL_GameControllerOpen(0);
SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);
return 0;
}
Works perfectly!
So I have a class that takes two template parameters, one is the type, one is the type of the function used, it has a reduce function to apply this function repeatedly to the array. However, I'm getting a compilation error.
function_template_test.cpp: In instantiation of 'class _C<int, int(int, int)>':
function_template_test.cpp:36:33: required from here
function_template_test.cpp:11:17: error: field '_C<int, int(int, int)>::op' invalidly declared function type
BinaryOperator op;
Here is my code. I have a class and the driver code down below in the main method.
#include<iostream>
template<typename _T>
_T addition(_T x,_T y)
{
return x+y;
}
template<typename _T,typename BinaryOperator>
class _C
{
private:
BinaryOperator op;
public:
_C(BinaryOperator op)
{
this->op=op;
}
_T reduce(_T*begin,_T*end)
{
_T _t_=*begin;
++begin;
while(begin!=end)
{
_t_=this->op(_t_,*begin);
++begin;
}
return _t_;
}
_T operator()(_T*begin,_T*end)
{
return this->reduce(begin,end);
}
};
int main(int argl,char**argv)
{
int arr[]={1,4,5,2,9,3,6,8,7};
_C<int,decltype(addition<int>)>_c_=_C<int,decltype(addition<int>)>(addition<int>);
std::cout<<_c_(arr,arr+9)<<std::endl;
return 0;
}
You're specifying function type as the template argument for BinaryOperator, which can't be used as the data member op's type; you can specifty function pointer type instead. e.g.
_C<int,decltype(addition<int>)*>_c_=_C<int,decltype(addition<int>)*>(addition<int>);
// ^ ^
BTW: Names like _C beginning with an underscore are reserved in C++.
Normally when assigning a function to a function pointer you don't explicitly need to add the address of operator (&) as it is not valid to assign the function itself to a variable so the language automatically adds it for you. However when doing decltype on a function name you do get the function type not a function pointer. For example try compiling the following, all the static_asserts should pass:
#include <type_traits>
void foo() {}
int main()
{
auto a = foo;
auto b = &foo;
static_assert(std::is_same_v<decltype(a), decltype(b)>,"a and b are function pointers");
static_assert(!std::is_same_v<decltype(a), decltype(foo)>,"foo is not a function pointer");
static_assert(std::is_same_v<decltype(a), decltype(&foo)>,"&foo is a function pointer");
}
Your code is essentially equivalent to:
#include <type_traits>
void foo() {}
int main()
{
decltype(foo) c = foo;
}
Which doesn't compile. Changing it to this fixes the problem:
#include <type_traits>
void foo() {}
int main()
{
decltype(&foo) c = foo;
}
The fix to your code is to change it to:
_C<int,decltype(&addition<int>)>_c_=_C<int,decltype(&addition<int>)>(addition<int>);
Or you can avoid repeating the types by just constructing directly:
_C<int,decltype(&addition<int>)>_c_(addition<int>);
Or using auto:
auto _c_=_C<int,decltype(&addition<int>)>(addition<int>);
I read few questions here on SO about this topic which seems yet confusing to me. I've just begun to learn C++ and I haven't studied templates yet or operator overloading and such.
Now is there a simple way to overload
class My {
public:
int get(int);
char get(int);
}
without templates or strange behavior? or should I just
class My {
public:
int get_int(int);
char get_char(int);
}
?
No there isn't. You can't overload methods based on return type.
Overload resolution takes into account the function signature. A function signature is made up of:
function name
cv-qualifiers
parameter types
And here's the quote:
1.3.11 signature
the information about a function that participates in overload
resolution (13.3): its parameter-type-list (8.3.5) and, if the
function is a class member, the cv-qualifiers (if any) on the function
itself and the class in which the member function is declared. [...]
Options:
1) change the method name:
class My {
public:
int getInt(int);
char getChar(int);
};
2) out parameter:
class My {
public:
void get(int, int&);
void get(int, char&);
}
3) templates... overkill in this case.
It's possible, but I'm not sure that it's a technique I'd recommend for
beginners. As in other cases, when you want the choice of functions to
depend on how the return value is used, you use a proxy; first define
functions like getChar and getInt, then a generic get() which
returns a Proxy like this:
class Proxy
{
My const* myOwner;
public:
Proxy( My const* owner ) : myOwner( owner ) {}
operator int() const
{
return myOwner->getInt();
}
operator char() const
{
return myOwner->getChar();
}
};
Extend it to as many types as you need.
As stated before, templates are overkill in this case, but it is still an option worth mentioning.
class My {
public:
template<typename T> T get(int);
};
template<> int My::get<int>(int);
template<> char My::get<char>(int);
No, you can't overload by return type; only by parameter types, and const/volatile qualifiers.
One alternative would be to "return" using a reference argument:
void get(int, int&);
void get(int, char&);
although I would probably either use a template, or differently-named functions like your second example.
Resurrecting an old thread, but I can see that nobody mentioned overloading by ref-qualifiers. Ref-qualifiers are a language feature added in C++11 and I only recently stumbled upon it - it's not so widespread as e.g. cv-qualifiers. The main idea is to distinguish between the two cases: when the member function is called on an rvalue object, and when is called on an lvalue object. You can basically write something like this (I am slightly modifying OP's code):
#include <stdio.h>
class My {
public:
int get(int) & { // notice &
printf("returning int..\n");
return 42;
}
char get(int) && { // notice &&
printf("returning char..\n");
return 'x';
};
};
int main() {
My oh_my;
oh_my.get(13); // 'oh_my' is an lvalue
My().get(13); // 'My()' is a temporary, i.e. an rvalue
}
This code will produce the following output:
returning int..
returning char..
Of course, as is the case with cv-qualifiers, both function could have returned the same type and overloading would still be successful.
You can think this way:
You have:
int get(int);
char get(int);
And, it is not mandatory to collect the return value of the function while invoking.
Now, You invoke
get(10); -> there is an ambiguity here which function to invoke.
So, No meaning if overloading is allowed based on the return type.
While most of the other comments on this problem are technically correct, you can effectively overload the return value if you combine it with overloading input parameter. For example:
class My {
public:
int get(int);
char get(unsigned int);
};
DEMO:
#include <stdio.h>
class My {
public:
int get( int x) { return 'I'; };
char get(unsinged int x) { return 'C'; };
};
int main() {
int i;
My test;
printf( "%c\n", test.get( i) );
printf( "%c\n", test.get((unsigned int) i) );
}
The resulting out of this is:
I
C
There is no way to overload by return type in C++. Without using templates, using get_int and get_char will be the best you can do.
You can't overload methods based on return types. Your best bet is to create two functions with slightly different syntax, such as in your second code snippet.
you can't overload a function based on the return type of the function.
you can overlead based on the type and number of arguments that this function takes.
I used James Kanze's answer using a proxy:
https://stackoverflow.com/a/9569120/262458
I wanted to avoid using lots of ugly static_casts on a void*, so I did this:
#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>
struct JoyDev {
private:
union {
SDL_GameController* dev_gc = nullptr;
SDL_Joystick* dev_js;
};
public:
operator SDL_GameController*&() { return dev_gc; }
operator SDL_Joystick*&() { return dev_js; }
SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
SDL_Joystick*& operator=(SDL_Joystick* p) { dev_js = p; return dev_js; }
};
struct JoyState {
public:
JoyDev dev;
};
int main(int argc, char** argv)
{
JoyState js;
js.dev = SDL_JoystickOpen(0);
js.dev = SDL_GameControllerOpen(0);
SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);
return 0;
}
Works perfectly!
I have this "better" enum class that
cannot contain invalid values, and
cannot be used until enum value is not set explicitly,
as follows:
class Symmetry
{
public:
enum Type {
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED
};
Symmetry() { t_ = UNINITIALIZED }
explicit Symmetry(Type t) : t_(t) { checkArg(t); }
Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; }
operator Type() const {
if (t_ == UNINITIALIZED) throw runtime_error("error");
return t_;
}
private:
Type t_;
void checkArg(Type t) {
if ((unsigned)t >= (unsigned)UNINITIALIZED)
throw runtime_error("error");
}
};
This allows me to write the following code:
Symmetry s1(Symmetry::SYMMETRIC);
Symmetry s2;
s2 = Symmetry::HERMITIAN;
Symmetry s3;
if (Symmetry::GENERAL == s3) // throws
My problem is that a compiler allows constructs such as:
Symmetry s1((Symmetry::Type)18); // throws
Symmetry s2;
s2 = (Symmetry::Type)18; // throws
I solved this problem by throwing exceptions, but I would prefer such a code not to compile at all (a compile time error). Is there a way how to manage this?
Potentially a crummy solution, but it would solve your immediate problem. Rather than having an inner enum type, define a little helper class with a private constructor, and make the outer class a friend. Then the "enum" values can be static const members in your outer class. Something like this:
(DISCLAIMER: untested, so there may be various compilation issues, but you should get the idea)
class Symmetry
{
public:
class Type
{
private:
Type() {};
friend class Symmetry;
};
static const Type GENERAL;
static const Type SYMMETRIC;
static const Type HERMITIAN;
};
You will need some way of determining equality, but this should be fairly easy.
My attempt using templates: (tested. However, this can be further improved!)
template<int N>
struct Symmetry
{
enum Type
{
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
template<Type e> struct allowed;
template<> struct allowed<GENERAL> { static const int value = GENERAL; };
template<> struct allowed<SYMMETRIC> { static const int value = SYMMETRIC; };
template<> struct allowed<HERMITIAN> { static const int value = HERMITIAN; };
template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; };
template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; };
allowed<(Type)N> m_allowed;
operator int()
{
return N;
}
};
Symmetry<0> e0; //okay
Symmetry<1> e1; //okay
Symmetry<100> e4; //compilation error!
Symmetry<e0.SKEW_HERMITIAN> e3; //okay
Symmetry<e0.SKEW_SYMMETRIC> e3; //okay
Usage:
int main()
{
Symmetry<0> e0;
Symmetry<e0.HERMITIAN> e1;
switch (e1)
{
case e0.HERMITIAN:
{
cout << "It's working" << endl;
}
break;
}
}
No. If you allow any cast to be used, as your last example does, then there will always be some cast that can be used to subvert your type.
The solution is to not be in the habit of using these casts and to very suspiciously consider any code that uses these casts indiscriminately. View this type of casting as the nuclear bomb in your arsenal: it's important to have, but you always handle it with care and never want to deploy it more than rarely.
What warning options does your compiler have for casting? What lint tools are you using which may detect this misuse of casts?
That said, it appears you really want to hide the inner Type so users are less tempted to even use it. Realizing that, it's straight-forward to make that type name private, even while not preventing all cast misuse, by slightly tweaking your original:
struct Symmetry {
enum {
UNINITIALIZED,
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
private:
typedef decltype(UNINITIALIZED) Hidden;
Hidden _value;
public:
Symmetry(Hidden value = UNINITIALIZED) : _value (value) {}
Symmetry& operator=(Hidden value) { _value = value; return *this; }
operator Hidden() const {
if (_value == UNINITIALIZED) {
throw std::logic_error("uninitialized Symmetry");
}
return _value;
}
bool initialized() const { return _value != UNINITIALIZED; }
// required if you want to check for UNINITIALIZED without throwing in
// the above conversion
};
This is a complete implementation, no details omitted or unknown, or issues with initialization order. The only caveat is decltype – with a pre-C++0x compiler, you'll have to use something implementation-specific or a library which wraps something implementation-specific.
And a smaller issue: change from runtime_error to logic_error, as using uninitialized values should be preventable beforehand and thus falls in the latter category.