I got compile error when passing shared_ptr<Derived>& as shared_ptr<Base>&, see the below code and detailed question.
Note: this question is similar to "Passing shared_ptr<Derived> as shared_ptr<Base>" but not duplicate.
#include <memory>
class TBase
{
public:
virtual ~TBase() {}
};
class TDerived : public TBase
{
public:
virtual ~TDerived() {}
};
void FooRef(std::shared_ptr<TBase>& b)
{
// Do something
}
void FooConstRef(const std::shared_ptr<TBase>& b)
{
// Do something
}
void FooSharePtr(std::shared_ptr<TBase> b)
{
// Do something
}
int main()
{
std::shared_ptr<TDerived> d;
FooRef(d); // *1 Error: invalid initialization of reference of type ‘std::shared_ptr<TBase>&’ from expression of type ‘std::shared_ptr<TDerived>’
FooConstRef(d); // *2 OK, just pass by const reference
FooSharePtr(d); // *3 OK, construct a new shared_ptr<>
return 0;
}
Compiled by g++ -std=c++11 -o shared_ptr_pass_by_ref shared_ptr_pass_by_ref.cpp
Env: Ubuntu 14.04, g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Detailed question:
Why is it OK to pass by const reference (*2), but not OK to pass by reference (*1)?
Note: I know the best practice is to pass by const reference, but just want to know why the compile error occurs.
You seem to expect some kind of template covariance, whereby AnyTemplateClass<Derived> can bind to AnyTemplateClass<Base>&. Templates don't work this way. Generally, AnyTemplateClass<Derived> and AnyTemplateClass<Base> are two distinct, completely unrelated classes.
A specific template class may, or course, provide a relationship in some form. shared_ptr<T> in particular has a templated constructor accepting shared_ptr<U> for any U such that U* is convertible to T*.
FooConstRef(d) call works by constructing a temporary - effectively
shared_ptr<TBase> temp(d);
FooConstRef(temp);
But temporaries can't bind to non-const references, that's why FooRef(d) doesn't work in a similar way.
Related
I was reading about encapsulated polymorphism and I came across a piece of code like that:
template <typename T>
struct Model<T> : Concept
{
Model<T>(T impl) :
mImpl(std::forward<T>(impl))
{
}
virtual Concept* clone() const override
{
return new Model<T>(mImpl)
}
virtual void operator (const LogMessage::Meta& meta, const std::string& message) override
{
mImpl(meta, message);
}
T mImpl;
};
What is the point of forwarding impl in Model constructor?
Does it make sense to forward an argument if it is passed by value?
If Model<T> where T is an lvalue reference type (e.g. X&) is legal (according to Model's documentation), then forward is the correct tool to use here. Otherwise (if T should always be an object type), move is the correct tool.
That being said, the clone member function makes it look like T should only be an object type. And so move would be a better tool to use here. And in this case forward isn't technically wrong, but simply confusing as it raises exactly the question the OP asks.
Having read couples of explanations here on StackOverflow, I still don't have a clue to how it works and what it is for. The demos I saw all use boost::reference_wrapper<int> as the type, that is, they all wrap int, and they also all run prefix op++ to show how it affects the wrapped int in the function template. One expert said the ref wrapper will be cast into the wrapped object if op++ is called on the ref wrapper, but it seems that it's not how it works. Please see the following example that demonstrates what would happen if the wrapped object is not an int. You may want to compile it before reading the code, so as to save your precious time.
// Build int version: g++ thisFile.cpp -Wall
// Build CFoo version: g++ -std=c++11 thisFile.cpp -DDONT_USE_INT -Wall
#include <boost/ref.hpp>
#include <iostream>
using namespace boost;
using namespace std;
class CFoo
{
public:
CFoo(int val) : m_val(val) {}
CFoo& operator++(void) {
++m_val;
return *this;
}
private:
int m_val;
friend ostream & operator<<(ostream& ostrm, const CFoo& param);
};
template <typename T>
void a_func_tmpl(T param)
{
++param;
}
ostream & operator<<(ostream& ostrm, const CFoo& param)
{
ostrm << param.m_val;
return ostrm;
}
int main(int argc, char *argv[])
{
#if defined(DONT_USE_INT)
CFoo obj(0);
#else
int obj(0);
#endif
a_func_tmpl(obj);
cout << obj << endl;
a_func_tmpl(ref(obj));
cout << obj << endl;
return 0;
}
Below is the output of compiling.
$ g++ -std=c++11 thisFile.cpp -Wall
$ ./a.out
0
1
$ g++ -std=c++11 thisFile.cpp -DDONT_USE_INT -Wall
thisFile.cpp: In instantiation of ‘void a_func_tmpl(T) [with T = boost::reference_wrapper<CFoo>]’:
thisFile.cpp:40:22: required from here
thisFile.cpp:22:2: error: no match for ‘operator++’ (operand type is ‘boost::reference_wrapper<CFoo>’)
++param;
^
As you can see, it does work if the wrapped type is int, but a compile-time error happens if the type is not int even if the wrapped type offers op++. It would be highly appreciated (I've been stuck on this for 2 days T_T) if someone can explain what really happens when a method of wrapped type is called on the wrapped ref. Thanks in advance. m(_ _)m
reference_wrapper is really simple, the easiest way to understand how it works is simply to look at the code. The generator functions, ref and cref that create a reference_wrapper are even simpler, again, just look at their definitions.
Understanding what it's for is also pretty simple: the intended use of reference_wrapper is to pass a variable by reference through a generic API that usually takes arguments by value. That's it.
That's useful when you have wrapped some function or functor in a forwarding API, and want to ensure the forwarding API passes references not values.
For instance, boost::bind copies its arguments into the callable object that it returns, and then invokes the target function passing the copied objects as arguments.
e.g. when you call boost::bind(&func, i) it returns a functor that contains a copy of &func and a copy of i. When you invoke that functor, it calls func with the copy of i. So if the function takes a reference, that reference is bound to the internal copy of i not to i itself. So if you have:
void func(int& i) { ++i; }
int i = 0;
auto bound = boost::bind(&func, i);
bound();
assert( i == 1 ); // FAILS!
The assertion will fail, because the int that gets passed to func is not i but a copy of i stored inside bound.
If you actually want the bound function to be called with a reference, you need something that is copyable like a value but implements reference semantics, which is where reference_wrapper comes in:
void func(int& i) { ++i; }
int i = 0;
auto bound = boost::bind(&func, boost::ref(i));
bound();
assert( i == 1 ); // passes
Now ref(i) creates a reference_wrapper<int> that refers to i and so bound contains a copy of that reference_wrapper<int>, also referring to i. When you invoke bound it passes the reference_wrapper<int> to func, which triggers the implicit conversion to int&, so that the reference binds to i, and i gets incremented as desired.
Other examples where you would use reference_wrapper are std::thread and std::async (and the Boost equivalents). They copy their arguments and then pass them to the wrapped target functor as rvalues, so if the functor has lvalue reference parameters then you must use reference_wrapper for the code to even compile.
Using reference_wrapper with your a_func_tmpl example doesn't really match the intended use, because the function doesn't take a reference, and you're not calling it through a generic API that would decay references to value anyway. Personally I wouldn't worry too much about why your example works in one case and not in the other, because it isn't the intended use case for reference_wrapper anyway. It's more important to understand what it is meant for, so you can use it in the appropriate places when necessary.
Your usage and understanding of reference_wrapper<> seems to actually be correct. However, you stumbled over another problem, which obscures that.
The problem is that there is no implicit conversion from reference_wrapper<CFoo> to CFoo& for the implicit this parameter. In this case, this would be needed to find operator++. However, it should work fine with a free-standing function that does the same:
void bar(CFoo& foo)
{
++foo;
}
template <typename T>
void a_func_tmpl(T param)
{
bar(param); // This allows the implicit conversion
}
Alternatively, you can implement operator++ as a free-standing function:
class CFoo
{
public:
CFoo (int val) : m_val (val) {}
private:
int m_val;
friend ostream & operator<<(ostream& ostrm, const CFoo& param);
friend CFoo& operator++(CFoo& foo);
};
CFoo& operator++(CFoo& foo) {
++foo.m_val;
return foo;
}
The only problem really is that the compiler doesn't know that it needs to convert from reference_wrapper<CFoo> to CFoo& to find operator++ if you define it in the class. The conversion is available, but it isn't asked for.
The code fails because ++param is calling boost::reference_wrapper<CFoo>::operator++ (because that's what you passed) for which there is no definition.
The interface of reference_wrapper<T> has a conversion operator to T&, but the compiler does not have a way to deduce that that's what you meant. x++ means 'call x::operator++', not 'find any old version of operator++ that I can coerce x into'
try
++(static_cast<T&>(param))
or
T& p = param;
++p;
A colleague asked me to look over some code he was writing and came across a problem with a particular line, which the compiler (g++) would complain about a function call not having a matching function based on its parameters.
After solving the problem in two ways (one by moving the parameter to its own variable and passing that instead, next is changing the parameter list to take it as a const reference), I had to ask this question: Why is the solution the way it is? I wasn't satisfied with writing it off as if some constructor details were being hidden away, as my colleague puts it.
As a result, I've replicated and reduced the problem to the following (compile with g++ -Wall -ansi -pedantic):
class SomeClass
{
public:
static void SomeFunction(SomeClass& sc) {}
static void SomeFunction2(const SomeClass& sc) {}
};
class SomeChild : public SomeClass {};
void testOne(void)
{
// this compiles
SomeChild sc = SomeChild();
SomeClass::SomeFunction(sc);
// this doesn't compile
//SomeClass::SomeFunction(SomeChild());
}
void testTwo(void)
{
// this compiles
SomeChild sc = SomeChild();
SomeClass::SomeFunction2(sc);
// this compiles
SomeClass::SomeFunction2(SomeChild());
}
int main(void)
{
testOne();
testTwo();
return 0;
}
I'm probably missing something very fundamental here, but can anyone explain to me why the compiler thinks there is no matching function call for the uncompilable line?
Thanks in advance.
The simple reason is that temporary values, such as the value of SomeChild(), cannot bind to non-constant lvalue references. While there's no deep technical reason for this, it's a design choice: non-constant references are usually used to modify the thing that's being referred to, and if that thing is temporary, then the modification would essentially have no lasting effect, which is almost always logic error.
Just replace 'SomeFunction2' by 'SomeFunction':
#include <iostream>
class SomeClass
{
public:
static void SomeFunction(SomeClass& sc) { std::cout << "not const" << std::endl; }
static void SomeFunction(const SomeClass& sc) { std::cout << "const" << std::endl; }
};
class SomeChild : public SomeClass {};
int main(void)
{
SomeChild sc = SomeChild();
SomeClass::SomeFunction(sc);
SomeClass::SomeFunction(SomeChild());
return 0;
}
And all is good.
Without the change you bind a temporary to a reference, that is impossible. The only alternatives are 'T', 'const T&' or a universal reference 'T&&' ( http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)
the following code compiles with Visual Studio 2008 but not with g++ on Mac OSX:
class A
{
public:
A CreateA()
{
A a;
return a;
}
};
class B
{
public:
void ComputeWithA
(
A &a // Here, removing the reference solves the problem
)
{
return;
}
};
int main ()
{
A a;
B b;
b.ComputeWithA(a); // Works
b.ComputeWithA(a.CreateA()); // Invalid arguments 'Candidates are: void ComputeWithA(A &)'
return 0;
}
Why this reference related issue? Any explanation would be greatly appreciated.
Best regards.
a.CreateA() gives you a R-Value (i.e. a temporary). ComputeWithA wants a reference, aka L-Value. The standard says that you can't convert R- into L-Values, so the MSVC is wrong here.
However, you can take a const reference since this case is explicitly allowed:
void ComputeWithA(A const &a) // add a const and everything works fine
The code isn't legal but VC is accepting it. Your function returns a temporary, which cannot be bound to a non-const reference (which your function takes as its argument).
Can you change your function to take its parameter by const reference?
If you can't change the signature of the method, there is a workaround
class A
{
public:
A& operator()()
{
return *this;
}
...
b.ComputeWithA(a.CreateA()());
I've used this once when implementing a logger, because the temporary object was used in more operations.
I have a class A
template<typename T>
class A : public std::auto_ptr<T>
{
typedef std::auto_ptr<T> Super;
public:
A() : Super() { }
A(T* t) : Super(t) { }
A(A<T>& o) : Super(o) { }
...
};
and IConstEnumerator
template<typename T>
struct IConstEnumerator
{
...
virtual IEnumerator<T>* GetEnumerator() = 0;
virtual IConstEnumerator<T>* GetEnumerator() const = 0;
};
When I run this code
AP< IConstEnumerator<IPort> > pe = node.GetPorts().GetEnumerator(); ;
I got errors from not finding correct match with g++ compiler.
error: no matching function for call to ‘AIR::A<AIR::IConstEnumerator<AIR::IPort> >::AP(AIR::A<AIR::IConstEnumerator<AIR::IPort> >)’
note: candidates are: AIR::A<T>::A(AIR::A<T>&) [with T = AIR::IConstEnumerator<AIR::IPort>]
note: AIR::A<T>::A(T*) [with T = AIR::IConstEnumerator<AIR::IPort>]
What's wrong with the class A? It works well with MSVC.
EDIT
Using copy constructor explicitly seems to solve this issue. A< IConstEnumerator<IPort> > pe(node.GetPorts().GetEnumerator())
Your A copy constructor takes its argument by non-const reference:
A(A<T>& o) : Super(o) { }
^ not const
Your example probably tries to copy a temporary object and the non-const reference can't bind to the temporary object. Visual C++ has an evil extension that allows this to work; you have to be careful to avoid relying on that extension if you want your code to be portable.
If you are trying to mimic auto_ptr's copy constructor, you need to also implement something similar to auto_ptr_ref, which is used as a helper to allow copying temporary auto_ptrs. I describe how this is accomplished in the accepted answer to How could one implement std::auto_ptr's copy constructor?
For what it's worth, deriving from std::auto_ptr is a bit odd; consider using composition instead of inheritance, if you can (there isn't a whole lot in std::auto_ptr that you'd benefit from by deriving from it anyway).
Without the exact code that actually causes the error it's hard to know for sure, but I suspect that from AIR::A<T>::A(AIR::A<T>&) what happening is you're trying to pass a temporary (possibly implicit) object into AIR::A<T>::A and MSVC lets you bind the temporary to the non-const reference parameter, while g++ quite properly prohibits this. Can you make the parameter const?
I'd guess you're lacking A(A<T> const & o).
VS (incorrectly) permits getting a non-const reference to a temporary, g++ behaves properly
It looks compiler talks that it cannot choose right function: constructor A from pointer A<T>::A(T*) or copy constructor A<T>::A(A<T> &). Maybe it worth to change definition to explicit A(T* t) : Super(t) { }