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)
Related
I am using the default constructor as an example here, but the same question applies to all the special member functions.
Also, I am using https://en.cppreference.com instead of the standard because that is what I am reading and that is what I am having trouble understanding. If using the standard would change the question in any way, please let me know.
As stated in https://en.cppreference.com/w/cpp/language/default_constructor, if a class has no constructor then the compiler will always declare a default one. Then, if certain conditions aren't met, it will be undefined (pre c++11) or defined as deleted (post c++11).
All this seems to imply that there is a difference between a function being not declared, declared but not defined, or declared and deleted. However, all three options won't compile/link, e.g.
class MyClass {
public:
void foo();
void bar() = delete;
};
int main() {
MyClass c;
//c.foo(); // won't link
//c.bar(); // won't compile
//c.baz(); // won't compile
}
So why is it so important to change the definition from "declared and undefined" to "declared and deleted", and why not just leave it as "undeclared" in the first place?
So why is it so important to change the definition from "declared and undefined" to "declared and deleted"
Because of the difference: "won't link" vs "won't compile". This is a fundamental reason for why = delete was introduced: To catch the bugs of using (previously) undefined functions at compile time, rather than later. Furthermore, it allows better error diagnostic, since the compiler will be able to describe why the function is deleted. The best that the linker can say is that nobody defined it for some unknown reason.
There's no reason not to take advantage of the feature with the implicitly generated member functions.
The = deleted; declaration can be useful in various situations. As well as the very good reason given by eerorika, it can also be used to explicitly declare that a given 'special' function, such as the default constructor doesn't exist, and cannot be called.
It can also be used to specify that a function which exists in a base class does not exist in a derived class (which, by default, it will).
Here's a short piece of code showing such usage:
#include <iostream>
class foo {
private:
int m;
public:
foo() = delete; // Other programmers reading this definition will know immediately!
foo(int n) : m{ n } {} // ... that default ctor is intended not to be used
void inc() { ++m; } // ... rather than it has been omitted accidentally
int get() { return m; }
};
class bar : public foo {
public:
bar() : foo(0) {}
void inc() = delete; // Without this, code like `bar b; b.inc();` will call foo.inc()
};
int main() {
// foo f1; // error C2280: 'foo::foo(void)': attempting to reference a deleted function
foo f2(3); std::cout << f2.get() << std::endl;
f2.inc(); std::cout << f2.get() << std::endl;
bar b1; std::cout << b1.get() << std::endl;
// b1.inc(); error C2280: 'void bar::inc(void)': attempting to reference a deleted function
return 0;
}
I just realized that I can use either to achieve the same effect. Are there any caveats? Which convention makes more sense?
#include <iostream>
using namespace std;
class some_class {
public:
void echo() {
cout << 1 << endl;
}
};
class other_class : public some_class {
public:
void echo() {
// Does it matter?
static_cast<some_class>(*this).echo();
static_cast<some_class&>(*this).echo();
}
};
int main() {
other_class a;
a.echo();
return 0;
}
The first cast creates a temporary object of type some_class, initialized from *this by slicing, then calls echo on the temporary object, then destroys the temporary object. If you made the echo function update a member variable of some_class, you would notice that *this actually did not get updated.
The second cast calls some_class::echo(); function on the *this object, without creating anything.
Typically the second option is what you are aiming for. As noted by davidhigh in comments, it is a cleaner code style (IMHO anyway) to simply write some_class::echo(); instead of using the cast.
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;
For this code
struct test {};
test f() { return test(); }
void print(test *x) {}
int main()
{
print(&f());
print(&test());
}
gcc-4.6 emits two "taking address of temporary [-fpermissive]" errors. This was introduced in 4.6, gcc-4.5 could compile it.
The reason is pretty clear and well documented. The problem is that it is a legacy code and, to compile, we have to make it work, thus, doing #pragmas around files and/or parts of code to compile them with -fpermissive. Let's say, customers are adamant not to modify the existing code (i.e. the fact of calling print() with &f() or &test() cannot be changed, not source files in general). In other words, one way or another this will be compiled, the only choice is more or less pain.
So the question is - are there any possible workarounds to make it work without doing -fpermissive in lots of places? -W flags, C++ tricks, etc.
What I meant is, the fact of calling print() with &f() or &test() cannot be changed.
If you have control over the type itself, you can always overload the reference operator, operator&, and return this from it. It's not a good thing to do in the general case, but it's fairly safe considering that you're returning the correct pointer of the correct type.
If base classes are involved, then it becomes rather more complicated. You'll need to use a virtual operator overload, and each class in the hierarchy will need to implement it separately.
You can provide a workaround by creating an extra print overload that will take a const&:
void print( test const & t ) {
print(&t); // Assuming that the function is 'print( test const * )'
// a print function should not take a non-const pointer!
}
And changing the caller to:
print( f() );
This requires code changes, but in a very limited form, so it might be acceptable. Also note that
Why not just rewrite the code to do exactly what the compiler used to do without complaining? In other words, store your temporary in a temporary (but addressable) variable.
struct test {};
test f() { return test(); }
void print(test *x) {}
int main()
{
test t1 = f();
test t2 = test();
print(&t1);
print(&t2);
}
This should behave identically to the way it did with the old compiler version. Tell the customer that the new compiler requires you to change the code to be explicit about something the compiler used to do implicitly.
It is not clear to me how much control you have over what, but the following hack seems to work:
Edit: Originally had f return a reference instead of a copy. DeadMG correctly points out this leads to undefined behavior as soon as the temporary gets used, so restored f back to returning a copy.
struct test {};
const test & test_copy (const test &t) { return t; }
#define test() test_copy(test())
test f() { return test(); }
#define f() test_copy(f())
void print(const test *) {}
int main()
{
print(&f());
print(&test());
}
The hack is to basically convert your temporaries into const references, so that the compiler will be happier. It shouldn't be made generally available, it's only purpose is to shut the compiler up. But, it doesn't really solve any underlying problems with the customer code or provided API. You could fix your API, and only resort to the hacks for certain customers:
struct test {};
test f() { return test(); }
void print(const test &) {}
#define CUSTOMER_NEEDS_HACK
#ifdef CUSTOMER_NEEDS_HACK
const test & test_copy (const test &t) { return t; }
#define test() test_copy(test())
#define f() test_copy(f())
void print(const test *t) { print(*t); }
#endif
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.