Here is my code :
#include <iostream>
template<typename B>
class test{
public:
B* t;
test(const B& init){
}
};
void funct(const int*& test){
}
int main(int argc, char** argv) {
test<int*> t(new int(2)); // works fine
funct(new int(2)); //error
return 0;
}
I am trying to emulate what's happening on my templated class, but somehow i get errors. I believe that const B& init with B as int* will be const int*& init. Adding const allowed it to accept temporary "new"s. I made a non-templated function to test it and it has this header, void funct(const int*& test). It is just the same as above but how come it wont take new int(2) ?
const int*& means "reference to pointer to const int. You need the const to apply to the pointer, not to the int:
void funct(int* const& test){}
This is what the template version does. T is a pointer to int, so T& is really a reference to pointer to int. The template does not perform a textual substitution of T for int*. It substitutes the type. You'd get the same if you used typedef int* X and const X&.
Related
I have the following base code:
Base Code
int main()
{
int i = 1;
const int* p = &i;
int* q = &i;
test_ptr(p);
test_ptr(q);
}
Can anyone explain why the first and third example work with the above base code, but the second one doesn't?
Example Implementation test_ptr()
Example 1 works. This works because function with pointer to const int will also accept a pointer to non-const int (but not the other way around)
void test_ptr(const int* p) // pointer to const int
{
}
Example 2 doesn't work. I don't really understand why. It is still a pointer to const int, but passed as a reference. This doesn't align with my understanding about how references work. It fails when I pass a non-const pointer to the function.
void test_ptr(const int*& p) // reference to pointer to const int
{
}
Example 3 works again and I am completely lost. So if case 2 does not work, why does it work again if I express the int* as a typedef?
typedef int* int_ptr;
void test_ptr(const int_ptr& p) // like case 2 but int* expressed as typedef
{
}
This also happens when I use pointer-to-pointer instead of reference-to-pointer.
Edit: Example 3 needs a different main function to make use of the typedef:
int main()
{
int i = 1;
const int_ptr p = &i; // use typedef here
int_ptr q = &i; // use typedef here
test_ptr(p);
test_ptr(q);
}
Example 2:
void test_ptr(const int*& p);
This works for const int* but not int* because the conversion from int* to a const int* implies a temporary and binding to a temporary has to be done using a const& for life extension to kick in.
Example 3 (when using the first main version):
typedef int* int_ptr; // or: using int_ptr = int*;
void test_ptr(const int_ptr& p);
This is the same as both of these:
void test_ptr(int_ptr const& p);
void test_ptr(int* const& p);
const is applied to the new type from right to left so it's not the int that is const, it's the pointer. The function will therefore accept int*, but not const int* since the function is allowed to change the int:s according to its signature.
The function that would accept both int* and const int* should have one of these equivalent signatures:
void test_ptr(const int* const& p);
void test_ptr(int const* const& p);
Disclaimer: I'm very unsure about the wording used in this answer
I have a class B that has two methods, where one returns a pointer to a member variable and the other returns a const reference to the variable.
I try to call those methods. During the calls, I store the return values to respective return types.
I was expecting the appropriate return types would end up calling the appropriate methods, but I get a compilation error saying:
error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
const int& refval2 = b.Get(); `
Here is my code:
#include <iostream>
class B{
public:
int* Get(){
return &x_;
}
const int & Get() const{
return x_;
}
private:
int x_ = 0;
};
int main(){
B b;
const int& refval2 = b.Get();
int* pval2 = b.Get();
}
Return type is not part of function signature, it's not considered in overload resolution.
In general, the candidate function whose parameters match the arguments most closely is the one that is called.
For non-static member function call, the type of the object to be called on involves too. There're two Get(), one is const and one is non-const. For b.Get();, the non-const Get() is an exact match; for the const Get() to be called the object b has to be converted to const. Then the non-const one wins, after that the compiler will try to convert the returned int* to const int& and fails.
So
B b;
int* pval2 = b.Get(); // call to non-const Get()
const B cb;
const int& refval2 = cb.Get(); // call to const Get()
Why is the int* Get() called insted of const int& Get()?
const int & Get() const
Is a const member function.
From class.this:
If the member function is declared const, the type of this is const X*
However, you declared:
B b;
as non-const which will call the non-const function int* Get(). Thus, the error.
There are two things
return type are not considered in overload resolution
Const ness of method is considered in over load resolution
If you remove const in second method, compiler will complain about ambiguity.
As already answered return type is not considered. So I see 3 possible solutions:
1 make it part of function signature:
class B {
public:
void Get( int *&refptr );
void Get( int &refval ) const;
};
int main(){
B b;
int refval2 = 0;
b.Get( refval2 );
int* pval2 = nullptr;
b.Get( pval2 );
}
but this produces pretty ugly code, so you better use method 2 or 3 -
2 use different function names, this is pretty obvious
3 use ignored parameter:
class B {
public:
int *Get( nullptr_t );
const int &Get() const;
};
int main(){
B b;
const int &refval2 = b.Get();
int* pval2 = b.Get( nullptr );
}
The problem is that the compiler thinks the two Get() functions are the same. If you create two identical functions with only varying return types, the compiler has no idea which function you want to use. To fix the problem, change the name of one or both of the Get() functions; say GetXPtr() and GetX().
class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&) /* adding reference is creating error */;
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
obj.funT(new B());
}
when compiling the above code with g++ compiler, I am getting the error: no matching function for call to A::funT(B)*. But when I remove the reference '&' operator from the funT() declaration as void funT(const T* obj), then it compiles and works fine. Why the reference operator is not allowed here?
You're asking for a reference to a pointer, you can't get references (unless they're C++11 rvalue references) to temporary values.
Make sure you have a lvalue passed as parameter to have the reference working.
e.g.
#include <iostream>
using namespace std;
class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&);
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
const B* ptr = new B(); // <-- This is an lvalue
obj.funT(ptr);
delete ptr; // Also clean it up after you used it
}
http://ideone.com/T4QJzi
Here is a more simple program which exhibits the same problem:
void fun(const int*&) {}
int main() {
int x;
fun(&x);
}
It yields the following error:
invalid initialization of non-const reference of type ‘const int*&’
from an rvalue of type ‘int*’
That makes sense. fun takes an argument of type "reference to non-const pointer to const int", and we tried to pass it a temporary of type "pointer to int". References to non-const types don't bind to temporaries, because temporaries are usually immutable. In this case, if we were allowed to pass &x as the argument to fun, then fun would be able to modify the address of x, which doesn't make any sense.
As you noticed, removing the & makes the code well formed:
void fun(const int*) {}
Now we are simply passing a pointer to int where a value of type pointer to const int is expected, which is a simple implicit conversion.
Alternatively, you might have intended for fun to take an argument of type "reference to const pointer to int":
void fun(int* const&) {}
Or a reference to const pointer to const int:
void fun(const int* const&) {}
But a reference to a const pointer is kind of silly, as a simple pointer would be just as good.
Finally, you could keep your original declaration of fun, and just avoid trying to pass a temporary as its argument.
The error msg is clear. Parameter type is of reference to pointer to T, but you are sending pointer to T. Temporaries cannot be passed as reference in this case. You can write:
int main() {
A<B> obj;
const B* b=new B(); //create a lvalue
obj.funT(b);
delete b; // make sure to release memory.
}
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.
Please consider the following code.
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t is a const variable and value() is a constant member-function which returns const T&. AFAIK, a const type is not assignable to a non-const type. But how foo* f = t.value(); compiles well. How this is happening and how can I ensure value() can be only assigned to const foo*?
Edit
I found that, this is happening on when templates are used. Following code works as expected.
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
Why the problem is happening when templates are used?
Because you have two levels of indirection - in your main function, that call to value returns a reference to a const pointer to a non-const foo.
This can safely be copied into non-const pointer to a non-const foo.
If you'd instantiated test with const foo *, it would be a different story.
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
Update
From the comment:
value() returns const T& which can
only be assigned to another const
type. But in this case, compiler is
safely allowing the conversion.
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
const int c = 5;
int n = c;
Here, I had some const data in c, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c has not been modified.
Now, suppose your foo had some data in it:
struct foo { int n; };
If I have a non-const pointer to one of those, I can modify the n value through the pointer. You asked your test template to store a pointer to a non-const foo, and then made a const instance of test. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test, so it cannot be made to point to another object. However, the object it points to can have its contents modified.
Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo * into each place where there's a T.
const T& value() const
Notice that you have a reference to a const T there. So the return value will be a reference to something const: a foo *. It's only the pointer address that can't be modified. The object it points to can have its contents modified.
In your second example, you got rid of the reference part, which changes the meaning and makes the const modifier apply to the object that the pointer points to, instead of applying to the pointer itself.
Use the following template specialization:
template<typename T>
class test<T*>
{
public:
test() {}
const T* value() const
{
return f;
}
private:
T* f;
};
After including this, g++ says:
d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’
There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your main function you try to change the address pointed to by the f member of t you'll see that you can't: encapsulation is perfectly preserved.
This is the same principle that makes the following code valid:
void foo(std::vector<int *> const & v)
{
*v[0] = 0; // op. [] returns const & to int *
}
People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.
The only solution is to do as Amit says and provide a specialization of your class for T*.