Creating a wrapper function for a constructor such as the following compiles just fine:
#include <iostream>
template <typename T>
class wrapper
{
public:
template <typename A0>
T* operator () (const A0& a0) const
{
return new T(a0);
}
};
class Foo
{
public:
Foo(int i) { std::cout << "Foo:Foo(" << i << ")" << std::endl; }
};
int main(int argc, char** argv)
{
wrapper<Foo>()(42);
return 0;
}
But the code does not compile when I update the line:
T* operator () (const A0& a0) const
to:
T* operator () (A0& a0) const
My guess is this has to do with the rvalue '42' not being bindable to to a non-const reference. But when I make the reference const this will mean that I could never call a constructor that actually takes a non-const reference. Can someone explain what is going on here, and what is the right thing to do to make it work?
My guess is this has to do with the rvalue '42' not being bindable to to a non-const reference.
Yes. Correct.
But when I make the reference const this will mean that I could never call a constructor that actually takes a non-const reference.
No. Incorrect. You can still call it with non-const reference. In fact, that is how const-ness works : non-const reference/pointer/object can implicitly convert into const reference/pointer/object, but vice-versa is not true.
So try it out.
Related
I have this little piece of code:
template<typename T>
class Test
{
public:
//operator T() const { return toto; }
T toto{ nullptr };
};
void function(int* a) {}
int main(int argc, char** argv)
{
Test<int*> a;
function(a);
return 0;
}
It doesn't compile unless the line operator T() const { return toto; } is un-commented. This magically works, but I am not sure why (if I un-comment the line).
I do understand that if the line is commented, the type of a when passed to function() is incompatible with the expected type int*. So, of course, the compiler complains ... no problem.
I also understand that the operator returns the actual type of the object, therefore in this particular case the compiler is happy.
I don't understand why the operator is called in this particular case.
Is doing function(a) the same thing as doing function(a()), only the () are implicit?
operator T() const { return toto; } is a user defined conversion operator, it is not operator(). It's used to define that your class is convertible to a different type.
operator() would look like this instead:
void operator()() const { ... }
In your case, you are using int* as T. If you substitute it yourself in the operator, you will see that it becomes operator int*() const { return toto; } which means "my class can be converted to an int* and the result of that conversion is evaluated as return toto;".
The function function() only accepts an int* as its argument. When you provide a Test instance, the call is only legal if there is a way to convert from Test to int*, which is why the operator T is required for the code to compile.
is it valid if I define member functions with same name¶meters but different return types inside a class like this:
class Test {
public:
int a;
double b;
}
class Foo {
private:
Test t;
public:
inline Test &getTest() {
return t;
}
inline const Test &getTest() const {
return t;
}
}
Which member function gets called if we have following code?
Foo foo; // suppose foo has been initialized
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
no, it is not valid, but in your example it is, because the last const is actually part of the signature (the hidden Foo *this first parameter is now const Foo *this).
It is used to access in read-only (get const reference, the method is constant), or write (get non-const reference, the method is not constant)
it's still a good design choice to return the reference of the same entity (constant or non-constant) in both methods of course!
No.
You cannot overload on return type.
Why? The standard says so.
And it actually makes sense - you can't determine what function to call in all situations.
Const and non-const methods with the same formal parameter list can appear side-by-side because the this pointer is treated as a hidden argument and would have a different type. This may be used to provide mutating and non-mutating accessors as in the code in your question.
If the signatures are exactly the same, then no.
To expand upon the previous answers and your given code with an example so you can actually tell what's being called when:
#include <iostream>
class Test {
public:
int a;
double b;
};
class Foo {
private:
Test t;
public:
inline Test &getTest() {
std::cout << "Non const-refrence " << std::endl;
return t;
}
inline const Test &getTest() const {
std::cout << "Const-refrence " << std::endl;
return t;
}
};
int main() {
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
const Foo bar;
const Test& t3 = bar.getTest();
return 0;
}
With output:
Non const-refrence
Non const-refrence
Const-refrence
The const you see after the second getTest signature tells the compiler that no member variables will be modified as a result of calling this function.
is it valid if I define member functions with same name¶meters but different return types [...]?
No.
Neither a method class nor a non-class function.
The reason is ambiguity. There would be situation in which the compiler could not pick the right overloading only by deducing the returned value.
In conclusion: you can't overload methods based on return type.
In your example, those two methods:
Test& getTest();
const Test& getTest() const;
Are correctly overloaded because the signature is different, but not because the return value is different!
Indeed, a function signature is made up of:
function name
cv-qualifiers
parameter types
method qualifier
So the signature of your methods are:
1) getTest();
2) getTest() const;
^------ Note that const qualifiers of the method is part of signature
As you can notice, the return value is not part of signature, but the const of the method qualifier is.
Which member function gets called if we have following code?
With the following code:
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
It will call only the no-const method, even in the case t2.
The reason is that foo object is no-const in that scope, so each
method will be called in its no-const form.
In details, in the third line:
const Test& t2 = foo.getTest();
foo.getTest() will return the no-const reference and after will
be implicitly converted in a const reference.
If you want to force the compiler to call the const version, you should "temporary convert" the object foo in a const.
For example:
const int& t2 = static_cast<const Foo&>(foo).getTest();
In that case I get a const ref to the object, so the object will be treated like a const and the proper const method will be invoked.
It seems it is in VS2013. But why in effective c++' item 3, the non-const operator calls const operator when they do exactly the same thing?
This is the code in Effective c++ item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return const_cast<char&>( // cast away const on type;
static_cast<const TextBlock&>(*this)[position] // add const to *this's type;call const version of op[]
);
}
...
};
A const object cannot use a non-const method. (Remember that operators are only methods).
But the converse is true: A non-const object can use a const method (although it will use a non-const equivalent if that's present).
A method is only const if it is marked as const: a compiler is not allowed to assert const-ness by inspecting the function body.
Yes: if a const member function is present, but no non-const version, then calling that function on a non-const object will use the const function.
#include <iostream>
struct Foo {
void bar() const { std::cout << "const bar()\n"; }
};
int main() {
Foo f;
f.bar();
}
prints const bar().
Note the reverse is not true. You may not call a non-const member function on a const object:
struct Foo {
void bar() { std::cout << "non-const bar()\n"; }
};
int main() {
const Foo f;
f.bar();
}
gives a compiler error:
SO.cpp: In function 'int main()':
SO.cpp:9:11: error: passing 'const Foo' as 'this' argument of 'void Foo::bar()' discards qualifiers [-fpermissive]
f.bar();
^
EC++ Item 3, Use const whenever possible has a subsection, Avoiding Duplication in const and Non-const Member Functions. The reason it suggests that you don't just let the compiler pick the const version is that the return types are different (by a const) - the non-const version casts this away:
class CharWrapper {
char c_;
public:
char const& getC() const
{
std::cout << "const getC()\n";
return c_; // pretend this line is complicated
}
char& getC()
{
std::cout << "non-const getC()\n";
char const& c = const_cast<CharWrapper const&>(*this).getC(); // call the right one to avoid duplicating the work
return const_cast<char&>(c); // fix the result type
}
};
int main() {
CharWrapper wrapper;
wrapper.getC() = 'a';
}
Notice that it calls const operator and casts away constness (it one of those few times where it does not result in UB). It is needed so non-const operator returns a mutable reference. Otherwise you would not be able to modify objects contained in vector at all.
Call to existing operator instead of writing code again is done to avoid code duplication and possible future errors when you change one function, but forgot the other.
Regarding your comment: there is no move involved. It returns a reference. I would expect no difference in generated code for both const and non-const operator.
I found in a C++ book the following:
Although we will not be doing it in this book, you can overload a
function name (or operator) so that it behaves differently when used
as an l-value and when it is used as an r-value. (Recall that an
l-value means it can be used on the left-hand side of an assignment
statement.) For example, if you want a function f to behave
differently depending on whether it is used as an l-value or an
r-value, you can do so as follows:
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
I tried this and it didn't work:
class Foo {
public:
int& id(int& a);
const int& id(int& a) const;
};
int main() {
int a;
Foo f;
f.id(a) = 2;
a = f.id(a);
cout << f.id(a) << endl;
}
int& Foo :: id(int& a) {
cout << "Bar\n";
return a;
}
const int& Foo :: id(int& a) const {
cout << "No bar !\n";
return a;
}
Have I wrongly understood it ?
Either the book's example is flat-out wrong, or you copied the wrong example from the book.
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
With this code, when you call s.f() where s is an object of type SomeClass, the first version will be called when s is non-const, and the second version will be called when s is const. Value category has nothing to do with it.
Ref-qualification looks like this:
#include <iostream>
class SomeClass {
public:
int f() & { std::cout << "lvalue\n"; }
int f() && { std::cout << "rvalue\n"; }
};
int main() {
SomeClass s; s.f(); // prints "lvalue"
SomeClass{}.f(); // prints "rvalue"
}
Ofcourse the book is correct. Let me explain the workings of an example of what the author meant :
#include <iostream>
using namespace std;
class CO
{
int _m;
public:
CO(int m) : _m(m) {}
int& m() { return _m; } // used as an l-value
int const& m() const { return _m; } // used as an r-value
};
int main()
{
CO a(1);
cout << a.m() << endl;
a.m() = 2; // here used as an l-value / overload resolution selects the correct one
cout << a.m() << endl;
return 0;
}
Output is
1
2
What you misunderstood is the function signature. You see when you have an argument &arg (as in id(&arg)) you pretty much predefine the l-valuness of it, so returning it through a const or non const member function does not change a thing.
The author refers to a common writting style that allows for 'getters' and 'setters' to be declared with a signature different only in const qualifires yet compile and behave correctly.
Edit
To be more pedantic, the following phrase
Recall that an l-value means it can be used on the left-hand side of an assignment statement.
is not valid anymore. lr valuness applies to expressions, and the shortest way to explain it, is that an expression whose adress we can take, is an l-value; if it's not obtainable it's an r-value.
So the syntax to which the author refers to, enforces the member function to be used correctly (correct compilation / overload resolution) at both sides of the assignment operator. This nowdays is no longer relevant to lr valueness.
A const member function can only be called on a const object. It makes no difference what you do with the return value. In your example, f is non-const, so it always calls the non-const version of f(). Note that you can also overload on r-value references (&&) in C++11.
I have the following piece of c++11-code:
#include <iostream>
struct object {
void talk(const char* text) const { std::cout << "talk " << text << std::endl; }
};
void makeItTalk(object& obj) { obj.talk("non-const"); }
void makeItTalk(const object& obj) { obj.talk("const"); }
template<typename P> void f(P&& p) {
makeItTalk(std::forward<P>(p));
}
int main() {
const object obj;
f(obj);
return 0;
}
When running I get talk const which is what it should be, but I'm wondering how it works. From what I read so far the const-qualifier is ignored in template deduction. Since obj is of type const object& and we have a P&& as parameter in f I would expect that the template parameter resolves to object& and since & && = & the function f should become
void f(object& p) { makeItTalk(std::forward<object&>(p)); }
But this function is not even allowed to be called for obj. So I'm wondering if I am wrong by saying the const is ignored?
As I understand it, type deduction does not ignore the const qualifier when the function template takes a pointer or reference parameter. The top-level consts are removed but not the constness of what is pointed to or referenced.
A more extensive argument can be found here: http://cpp-next.com/archive/2011/04/appearing-and-disappearing-consts-in-c/