Wrong function prototype used by compiler? - c++

I faced a compilation problem that I do not understand, I have simplified it a bit for explanation below.
Basically, it involves having 2 different getters (a const and non-const one) that return a container (a map in this example) with const, respectively non-const value_type.
What puzzles me is that in the example below, the compiler seems unable to use the const getter on a non-const object:
#include "stdafx.h"
#include <utility>
#include <map>
class TestObject
{
public:
TestObject() {}
virtual ~TestObject() {}
};
typedef std::pair<const TestObject*, const TestObject*> ConstTestObjectPair;
typedef std::pair<TestObject*, TestObject*> TestObjectPair;
class Test
{
TestObject* m_pObject;
public:
Test() {m_pObject = new TestObject();}
virtual ~Test() {delete m_pObject;}
std::map<unsigned, ConstTestObjectPair> GetObject() const
{
std::map<unsigned, ConstTestObjectPair> map;
map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
return map;
}
std::map<unsigned, TestObjectPair> GetObject()
{
std::map<unsigned, TestObjectPair> map;
map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
return map;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Test* pTest = new Test();
const Test* pConstTest = pTest;
std::map<unsigned, ConstTestObjectPair> CTO = pTest->GetObject(); // Not compiling, I don't get why!!!
CTO = pConstTest->GetObject();
std::map<unsigned, TestObjectPair> TO = pTest->GetObject();
//TO = pConstTest->GetObject(); // Not working, this is expected
return 0;
}
I tried with both VS2010 and gcc and neither accepts to compile this code. Here is the compilation error returned by VS2010:
1>c:\test.cpp(48): error C2440: 'initializing' : cannot convert from 'std::map<_Kty,_Ty>' to 'std::map<_Kty,_Ty>'
1> with
1> [
1> _Kty=unsigned int,
1> _Ty=TestObjectPair
1> ]
1> and
1> [
1> _Kty=unsigned int,
1> _Ty=ConstTestObjectPair
1> ]
1> No constructor could take the source type, or constructor overload resolution was ambiguous
Could someone explain me why the compiler cannot find/use the correct prototype on the non-const object?
Thanks a lot!

If you're really curious, check out section 13.3.3 of the C++03 standard, which describes how the "best viable function" is determined. Here are some important points:
The selection criteria for the best function are the number of arguments, how well the arguments
match the types of the parameters of the candidate function, how well (for nonstatic member functions) the
object matches the implied object parameter, and certain other properties of the candidate function. [Note:
the function selected by overload resolution is not guaranteed to be appropriate for the context. Other
restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. ]
And later:
If there is exactly one viable function that is a better function than all other viable functions, then it is the
one selected by overload resolution
Note that the return type of the function is not mentioned in this criteria. So the non-const method is selected as most valid, because its "implied object parameter" (essentially the "this" pointer) is non-const. This all happens before the conflict with the return type is detected.
To solve this problem, I would either:
Change your design so that ConstTestObjectPair isn't needed, and you can just use const TestObjectPair (preferred solution)
Cast your non-const objects to const ones when needed

What puzzles me is that in the example below, the compiler seems unable to use the const getter on a non-const object
It is not "unable" but required to chose the other one.
Overloading is selected using the passed actual parameters. For member function including the hidden param used for this. For T* the non-const overload is selected, if you want the other you must use a const T* by cast or other means.
Actually it is a common mistake to think the return type will be used some way and the function that returns what you want to use in the expression gets selected. It is just not so.

The problem is quite simple. pTest is a pointer to an object of type Test, which is not const. Hence in the call pTest->GetObject() the non-const member function is selected, that is
std::map<unsigned, TestObjectPair> GetObject()
As you see, this function returns a value of type std::map<unsigned, TestObjectPair>. But then you try to initialize the variable CTO of type
std::map<unsigned, ConstTestObjectPair>
with this value. To do that, the compiler needs to convert the returned value to this type. But there is no conversion constructor to do that. And that is what the compiler error tells you.

C++ compiler will choose the explicit overrided method, so here pTest is a non-const viable and pConstTest is const one.
Test* pTest = new Test();
const Test* pConstTest = pTest;
pTest->GetObject will choose the non-const GetObject:
std::map<unsigned, TestObjectPair> GetObject()
{
std::map<unsigned, TestObjectPair> map;
map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
return map;
}
pConstTest->GetObject() will choose the const GetObject:
std::map<unsigned, ConstTestObjectPair> GetObject() const
{
std::map<unsigned, ConstTestObjectPair> map;
map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
return map;
}
the first error happened when you assign a returned
std::map<unsigned, TestObjectPair> value
to a
std::map<unsigned, ConstTestObjectPair> viable

Related

Difference between const and non-const method?

int CRegister::CountCars(const string& name, const string& surname)const{
const pair<string,string> wholename(name,surname);
vector<CDriver>::iterator Diterator=lower_bound(m_Drivers.begin(),m_Drivers.end(),wholename);
if (Diterator<m_Drivers.end()){
if(Diterator->m_name.compare(wholename.first)!=0 || Diterator->m_surname.compare(wholename.second)!=0) return 0;
return Diterator->m_DriversNumber;
}
return 0;
}
Hello, when I try to compile this, it throws error on the third line:
"conversion from ‘__gnu_cxx::__normal_iterator<const CDriver*, std::vector<CDriver> >’ to non-scalar type ‘std::vector<CDriver>::iterator {aka __gnu_cxx::__normal_iterator<CDriver*, std::vector<CDriver> >}’ requested
When I set the function CountCars as a non-const, it compiles without problems. What should I change to fix this? (the function has to be const)
To solve your problem you have to use a const_iterator
The reason is the following : The method is marked const, which means that the method itself is not going to change state of the object instance upon which the method is invoked.
Therefore within a const method you cannot call any other method on the same object that is not marked const. Because of course that new call does not garantee that it is const so can the first method not claim to be const anymore.
by declaring the iterator const you are going to use the const version of lower_bound.
Try using a const_iterator:
vector<CDriver>::const_iterator Diterator
// ^^^^^^
Consider using a const_iterator, e.g.
vector<CDriver>::const_iterator Diterator
= lower_bound(m_Drivers.begin(), m_Drivers.end(), wholename);
If you can compile in C++11/14, using auto helps as well:
auto Diterator = lower_bound(m_Drivers.begin(), m_Drivers.end(), wholename);
(With auto, the compiler deduces the correct iterator type, without requiring you to correctly "spell" it explicitly in code.)

c++ no operator matches even when operator is defined

I am trying to implement custom reference in C++. What I want to achieve is to have reference, which does not need to be set at creation. It looks like this
template<typename T>
class myreference
{
public:
myreference() : data(), isset(false) { }
explicit myreference(const T& _data) : data(&_data), isset(true) { }
myreference<T>& operator=(T& t)
{
if (!isset)
{
isset= true;
data = &t;
}
else
*data = t;
return *this;
}
operator T() const { return *data; }
operator T&() { return *data; }
private:
T* data;
bool isset;
};
It works fine. I can do this, except for last statement.
myreference<int> myref;
int data = 7, test = 3;
myref = data; // reference is set
myref = test; // data is now 3
int& i = myref;
i = 4; // data is now 4
cout << myref; // implicit int conversion
myref = 42; // error C2679: binary '=' : no operator found which takes a right-hand operand of type 'int'
Full error
error C2679: binary '=' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
1> d:\...\main.cpp(33): could be 'myreference<int> &myreference<int>::operator =(const myreference<int> &)'
1> d:\...\main.cpp(16): or 'myreference<int> &myreference<int>::operator =(T &)'
1> with
1> [
1> T=int
1> ]
1> while trying to match the argument list 'myreference<int>, int'
I was searching through the web and found similar errors (with different operators) and the result was to put the operator outside of its class, but that is not possible for operator= for some (I believe good) reason. My question is, what is the compiler complaining about? Argument list is myreference<int>, int and I have operator defined for Ts which, in this case, is int.
You instantiated an object of type myreference<int>
myreference<int> myref;
So the assignment operator is specialized for parameter type int &. However you are trying to assogn a temporary object that may be bound to a constant reference. So the assignment operator has to be specialized for parameter type const int &.
Thus you need to define another object of type myreference<const int> that the code would be at least compiled. For example
myreference<const int> myref1;
myref1 = 42;
However in any case the code will have undefined behaviour because the temporary object will be deleted after the executing this assignment statement and data member data will have invalid value.
Usually such claases suppress using temporary objects that to avoid the undefined behaviour.
Your problem is bigger than the compiler error. The compiler error is telling you that you have a fundamental design error.
Your reference = both acts as a reference rebinder (changes what an empty reference is attached to) and an assignment (chanhes the value of the thing attached to). These are fundamantally different operations.
One requires a long-term value you can modify later (rebind), and the other requres a value you can read from (assignment).
However with one function, the parameter passed has to be either -- and those types do not match. A readable from value is a T const&. A bindable value is a T&.
If you change the type to T const& the rebind data = &t fails, as it should. If you pass a temporary, like 42, to your =, a rebind attaches to it, and becomes invalid at the end of the calling line. Similarly if you assign int const foo = 3; to it.
You can 'fix' this with a const_cast, but that just throws the type checking out the window: it hides the undefined behaviour instead of giving you a diagnosis.
The T& parameter, on the other hand, cannot bind to a 42 temporary, nor can it bind to a constant const int foo = 3. Which is great in the rebind case, but makes assignment sort of useless.
If you have two operations, =(T const &) and .rebind(T&), your problem goes away. As a side effect, = when not set is indefined behaviour. But that was basically true before.
Your reference is probably better called a 'pointer' at this point.
myreference<T>& operator=(T& t)
myref = 42;
non-const reference can't be bound to a temporary. You can change it to take parameter by const reference.
myreference<T>& operator=(const T& t)
Also, make changes in your code to make this work.
data = &t; <<< Replace this
data = const_cast<T*>(&t); <<< with this.
Since assignment operator is expecting variable (in this case an integer) you are not allowed to pass it some value(42).
So use an integer like
int x=42;
myref=x;
Happy coding.

Compiler says object is const, I can't see how

I'm having trouble with this code:
NonCommutativePolynomial<SR> differential_at_wrt_variable
(std::map<VarId,SR> valuation, VarId variable) {
NonCommutativePolynomial<SR> result;
for(auto &monomial : monomials_) {
result += monomial.first.differential_at_wrt_variable(valuation, variable)
* monomial.second;
}
return result;
}
monomials_ has the type std::map<NonCommutativeMonomial<SR>,std::uint_fast16_t>.
In the line result += ... I'm getting this compiler error:
error: passing ‘const NonCommutativeMonomial’ as ‘this’
argument of ‘NonCommutativeMonomial
NonCommutativeMonomial::differential_at_wrt_variable(std::map&, VarId&) [with SR = LossySemiring]’ discards qualifiers
[-fpermissive]
Now I realize that this means that I am calling a method (function?) on a constant object where the method does not guarantee that it won't modify the object. What I don't understand is how monomial.first can be constant when I haven't declared it to be thus anywhere. Any ideas what I might be doing wrong?
EDIT:
See the answers below why monomial.first is constant. I need a non-constant copy of it, the class NonCommutativeMonomial<SR>has this copy constructor:
NonCommutativeMonomial(const NonCommutativeMonomial &m) = default;
However, when I call that using
NonCommutativeMonomial * mono = new NonCommutativeMonomial<SR>(monomial.first);
and work with mono afterwards, I still get the same error.
In std::map keys are constant objects and you must never change them.
EDIT:
I'd suggest the following change:
NonCommutativePolynomial<SR> result;
for(auto &monomial : monomials_) {
NonCommutativePolynomial<SR> tmp(monomial.first);
result += tmp.differential_at_wrt_variable(valuation, variable)
* monomial.second;
}
it seems that member function differential_at_wrt_variable has no qualifier const while monomial.first is a const object.
Value type is defined for std::map as
typedef pair<const Key, T> value_type;
that is Key has qualifier const.
The value_type of a std::map<KeyType, MappedType> is declared as a std::pair<const KeyType, MappedType>.
An iterator for a std::map (which is what your for loop is using) is over the map's value_type.
In your case this results in monomial.first having the type const NonCommutativeMonomial<SR> which, as you recognise, cannot be used with a non-const member function.
The reason for the key in the value type being const is to prevent the map keys themselves being modified during the iteration.

C++, decltype and constness error

I can't understand why the following code is wrong.
struct A{
typedef std::vector<std::vector<int>> Base;
// const auto& func(std::size_t e) const
auto func(std::size_t e) const -> decltype(std::declval<Base>()[e])
{
return base[e];
}
Base base;
};
I get compile error with the above snippet in gcc 4.8.1. (error: invalid initialization of reference of type '__gnu_cxx::__alloc_traits > >::value_type& {aka std::vector&}' from expression of type 'const value_type {aka const std::vector}'
return base[e];)
Note that if I delete the const qualifier, it works fine.
But if I replace the part for function signiture by the commented one (to use the automatic type deduction introduced in C++14). No error is generated.
So, I guess the decltype part is wrong.
Since Base is a non-const type, std::declval<Base>()[e] refers to a non-const version of operator[] of std::vector. That version of [] returns a non-const reference of type std::vector<int> &. So, the return type of your function is declared as decltype(std::declval<Base>()[e]), which is std::vector<int> &.
Meanwhile, your member function func is declared as const. It means that member base will have const Base type inside that member function. This in turn means that the application of operator [] to base will refer to const version of operator []. That version of [] returns the result of const vector<int> & type.
So, your return statement attempts to implicitly convert a const vector<int> & value to a vector<int> & value. This conversion is invalid. It violated the rules of const-correctness.
Either add const in the return type, as #catscradle suggested. Or remove const from method declaration
auto func(std::size_t e) -> decltype(std::declval<Base>()[e])
Either this or that.
This works:
auto func(std::size_t e) const -> decltype(std::declval<const Base>()[e])

Why does C++ allow but ignore the application of const to function types?

I get a real kick out of exploring the unusual corners of C++. Having learned about the real types of functions rather than function pointers from this question, I tried messing around with function typing and came up with this bizarre case:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
const Func*& f = &Foo;
return 0;
}
Since &Foo is an rvalue of type Func*, I figured that I should be able to put it in a const reference, but I get this error from g++ 4.6:
funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’
But f is const! It has become apparent to me that the application of const to a function (or reference/reference to pointer etc.) is simply ignored; this code compiles just fine:
template <typename A, typename B>
struct SameType;
template <typename A>
struct SameType<A, A> { };
typedef int Func(int);
int main()
{
SameType<const Func, Func>();
return 0;
}
I'm guessing this is how boost pulls off their is_function type trait, but my question is - why does C++ allow this by ignoring it instead of forbidding it?
EDIT: I realise now that in the first example f is non-const and that const FuncPtr& f = &Foo does work. However, that was just background, the real question is the one above.
But f is const!
No, it's not. You're confusing
const Func*& f = &Foo;
with
Func* const& f = &Foo;
The former is a non-const ref to a const pointer. The latter is a const ref to a non-const pointer.
That's why I always write the const-ness before the */& rather than before the type. I would always write the first case as
Func const*& f = &Foo;
and then read right to left: reference to a pointer to a const Func.
In c++03 it was not ignored, but illformed (and was an sfinae case). I guess they changed that in c++11 because then you can simply have function parameters be const F& and can pass to it rvalue function objects aswell as normal functions.
See this DR which made the change http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#295
&Foo is a pointer. In general, I would suggest avoiding references to pointers (const or no). At least, not unless you know what you're doing.
So you should have:
const Func *f = &Foo;
Or really, you can ditch the const entirely:
Func *f = &Foo;
why does C++ allow this by ignoring it instead of forbidding it?
Because you're talking about two different things.
In C++, there is a difference between a function type and a function pointer. Foo is a function type, specifically int(int). &Foo is a function pointer, of type int(*)(int). A function type degrades into a function pointer, where necessary (much like array types degrade into pointers). But they are distinct (just like arrays).
So your two cases are not the same. Your first case is dealing with a function pointer, and your second case is dealing with a function type (which is what the template argument is deduced as).
As for why function types swallow the const, that's because the values of function types are already implicitly constant. You can't change them. The only operation you can perform on a function type is () (or conversion to function pointer). So a const T is equivalent to T if T is a function type. Visual Studio 2010 actually gives a warning about that.
The following compiles fine:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
Func* const& f = &Foo; //ok
return 0;
}
Be aware that const statements are evaluated along with pointers and references from right to left. The last const to the very left you wrote translates to last possible position right of a Name (C++ FAQ on const placement). Hence
const Func*& f
Is translated by the compiler to
Func const*& f
Hence you get a reference to a constant pointer which is not what you want. Besides I would not use references to function pointer if you do not really have to.
No, f is not const. First of all, it is a reference to some mutable type (that mutable type happens to be a const pointer, i.e. a mutable pointer to something that you promise not to change through that particular pointer). However, with &Foo you are creating a temporary (of type Func*) and you cannot assign a temporary to a mutable reference. You can only assign it to a const reference. And that is precisely what the error message is telling you.
Sometimes the error messages can be a bit cryptic.
I put together an example on ideone to illustrate the types printed by gcc for a variety of things:
#include <iostream>
typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;
template <typename T> void DisplayType() { T::foo(); }
int main() {
DisplayType<Func>();
DisplayType<FuncPtr>();
DisplayType<FuncPtrRef>();
DisplayType<FuncPtrConstRef>();
}
The compilation errors give:
Func is of type int ()(int) (not a valid type, should now be fixed in gcc)
FuncPtr is of type int (*)(int)
FuncPtrRef is of type int (*&)(int)
FuncPtrConstRef is of type int (* const&)(int)
In your error message you have int (*&)(int), which is a reference to a non-const pointer to a function type.
You are not allowed to bind an rvalue to a non-const reference.
Note: this has thus nothing to do with const being swallowed, as smparkes correctly diagnosed