C++ template member function having pointer reference template parameter - c++

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.
}

Related

Multiple explicit constructors and implicit conversion

I have a class like this:
class Foo
{
private:
std::string m_data;
public:
Foo() = default;
explicit Foo(double value);
explicit Foo(float value);
explicit Foo(int64_t value);
explicit Foo(bool value);
explicit Foo(const std::string& value);
explicit Foo(const char* value);
};
... because I'd like to be able to construct Foo instances from integers, booleans, floats, doubles, and so on.
Let's say I have another class Bar, which has a method that takes a reference to const Foo:
void append(const Foo& foo);
Why does the compiler complain when I do the following?
instanceOfBar.append(3.5);
Reference to type Foo could not bind to an rvalue of type 'float'
instanceOfBar.append(4);
Reference to type Foo could not bind to an rvalue of type 'int'
instanceOfBar.append(true);
Reference to type Foo could not bind to an rvalue of type 'bool'
I am using GCC 12.2.1
Your constructors of the class Foo are declared with the function specifier explicit. So, you need to convert argument expressions explicitly to the type Foo to allow the function append() to bind them to the const lvalue reference.
As, for example:
instanceOfBar.append( Foo( 4 ) );
In fact, you are trying to do something like the following:
struct A { explicit A( int ) {} };
const A &a = 10;
This code will not compile because the compiler can not implicitly convert the integer constant 10 to the type A.
But, if you remove the function specifier explicit in the constructor declaration, then this code snippet will compile:
struct A { A( int ) {} };
const A &a = 10;

Pointer by reference on templates and normal functions

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&.

Passing a pointer to temporary object

We know that we can pass temporary objects to functions by const reference, like this:
class A
{
public:
A(int _b = 0)
{
b = _b;
}
int b;
};
void foo(A& a) {printf("%d", a.b);}
void cfoo(const A& a) {printf("%d", a.b);}
int main(void)
{
//foo(A(4)); doesn't compile
cfoo(A(5));
}
but what about passing by pointer?
why does this compile?
void pfoo(A* pa) {pa->b = 19;}
int main(void)
{
pfoo(&A(5));
}
but what about passing anonymous variables pointer? why does this compile?
You are probably using a compiler that does not honour C++ standard.
No address of an r-value (temporary) object can be taken. That should not compile.
However, operator& can be overloaded, so that it can be invoked on a temporary object, e.g.:
struct A
{
A* operator&() { return this; }
};
In C++11 a temporary object can be bound to an r-value reference. After that r-value reference behaves like an l-value and hence the address of a temporary object can be taken:
struct A {};
void foo(A*);
void foo(A&& a) { foo(&a); }
int main() {
foo(A{});
}

C++ polymorphism with boost scoped_ptr

Why does the following code not allow foo(ptr) to be called ?
#include <boost/scoped_ptr.hpp>
struct A {
virtual ~A() {}
};
struct B: public A {};
void foo(boost::scoped_ptr<A>& a) {}
void goo(A& a) {}
int main() {
boost::scoped_ptr<B> ptr(new B);
foo(ptr);
B b;
goo(b);
}
The corresponding form where we pass references works as expected. Are we supposed not to do polymorphism
with boost scoped_ptr ?
g++ with boost 1.49 gives me:
error: invalid initialization of reference of type ‘boost::scoped_ptr<A>&’ from expression of type ‘boost::scoped_ptr<B>’
That's because foo, for some reason, takes a scoped pointer by reference. That is completely unnecessary and is the reason why the call fails. There is a conversion from scoped_ptr<B> to scoped_ptr<A> but not from scoped_ptr<B>& to scoped_ptr<A>&.
You should pass it as reference to const.
void foo(boost::scoped_ptr<A> const & a) {}
Incidentally, this isn't a "problem" of smart pointers per se. The following code fails for the same reasons as yours.
void foo(A*& p) {}
int main()
{
B* p = new B;
foo(p); //FAIL
}
In order to fix this you have to pass the pointer either by value, or, if you're sufficiently perverted, by reference to const
void foo (A * const & p); // <-- a perv wrote this

const correctness and return values - C++

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*.