C++, decltype and constness error - c++

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])

Related

result_of does not define type for mem_fn

I have the following piece of code:
#include <functional>
struct X {
int get() const& {
return 42;
}
};
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
X x;
return fn(x);
}
int main(void) {
Apply([](X const& x){return x.get();});
//Apply(std::mem_fn(&X::get)); // does not compile
}
The first call to Apply compiles fine, but if I uncomment the second call, I get the following compilation error:
main.cpp:16:5: error: no matching function for call to 'Apply'
Apply(std::mem_fn(&X::get)); // does not compile
^~~~~
main.cpp:10:27: note: candidate template ignored: substitution failure [with Func = std::_Mem_fn<int (X::*)() const &>]: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)() const &> (X)>'
std::result_of_t<Func(X)> Apply(Func fn) {
^
I somehow expected that both calls could be used interchangeably and that std::mem_fn just "would do the right thing". Can anybody explain, what happens here?
The problem is here:
int get() const& {
// ^^^
Your member function is lvalue-reference qualified. In your Apply():
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
return fn(X{});
}
you're invoking it with an rvalue. Which brings us to the [very surprising to me] difference between these two expressions:
X{}.get(); // ok
(X{}.*&X::get)(); // ill-formed
On specifically pointer-to-member operators, the ref-qualifiers of the member pointer are checked against the value category of the object. From [expr.mptr.oper]:
In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &. In a .* expression whose object expression is an lvalue, the
program is ill-formed if the second operand is a pointer to member function with ref-qualifier &&.
So the first expression is okay, get() is const&-qualified but rvalues can bind to that. The second expression is not okay - the rules just explicitly prohibit it.
So the behavior you see is perfectly correct - mem_fn is defined by directly invoking the member function, which is ill-formed on an rvalue, so Apply is removed from the overload set. If it were not, then instantiating the body would be a hard error.
The reason the lambda works is that the temporary X is bound to the lambda's reference parameter. get() is then invoked on the lvalue function parameter - not on the temporary passed into it. But even without that, invoking get() directly on the temporary would still be fine.

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

function returning reference to a vector element

I cannot figure out how to return a reference to a vector element. The [] and at() are returning reference, no?
But when I try the following, it won't compile.
I'm using Visual C++ and it gives cannot convert from 'const float' to 'float & error.
T& GetElement(size_t x) const {
return _vector.at(x);
}
GetElement is a method, and _vector is a member variable.
This does not compile because you are trying to return a non-constant reference to an element of the vector, which is itself const.
The reason the vector is const is that your member function is declared const:
T& GetElement(size_t x) const // <<== Here
This const-ness marker gets propagated to all members, including the _vector.
To fix this problem, add const to the type of the reference being returned (demo).
You haven't shown your error, but as a guess, it looks like _vector is a member (from the _ prefix you've given it) and you're in a const member function, so at will return a const reference. So you probably need:
const T& GetElement(size_t x) const {
return _vector.at(x);
}

Return value syntax overloading const and not const function

I have a problem providing the correct overloading for const and not const getter functions with the new return value syntax.
In my class PhysicalNode I have defined a getter function with the new return value syntax. This is needed as the return type of the getter depends on the type of the member.
class PhysicalNode {
private:
solver::EnergySolver energySolver_; ///< The energy solver of this node
//solver::EnergyMomentumSolver energySolver_
public:
auto getEnergySolver()-> typename
std::add_lvalue_reference<decltype(PhysicalNode::energySolver_)>::type;
}
However I want now to also provide this method as const.
Normally I would use function overloading to define my const and not const getter function like that.
class PhysicalNode {
private:
solver::EnergySolver energySolver_;
public:
const solver::EnergySolver& getEnergySolver() const;
solver::EnergySolver& getEnergySolver();
}
I have tried the following function declaration but it does not work:
const auto getEnergySolver() const-> typename
std::add_lvalue_reference<decltype(PhysicalNode::energySolver_)>::type;
The compile error is:
PhysicalNode.cpp:72: error: invalid initialization of reference of type
'std::__add_lvalue_reference_helper<LbmLib::solver::EnergySolver, true,
false>::type {aka LbmLib::solver::EnergySolver&}' from expression of type
'const LbmLib::solver::EnergySolver'
How do I need to define the function declaration to define this function as constant.
If you really really want to use this notation and the standard type traits, you should write your const overload this way:
auto getEnergySolver() const ->
std::add_lvalue_reference<
std::add_const<decltype(PhysicalNode::energySolver_)>::type
// ^^^^^^^^^^^^^^
>::type;
Otherwise you would be returning a reference to non-const, which is clearly wrong considering your member function is const-qualified.
Notice, however, that type traits are not really needed here (if EnergySolver is just a regular type and not a reference type alias):
auto getEnergySolver()-> decltype(PhysicalNode::energySolver_)&;
auto getEnergySolver() const -> decltype(PhysicalNode::energySolver_) const&;
But even decltype is unnecessary. If your real program is not more complicated than the example you are showing, this is enough:
auto getEnergySolver()-> solver::EnergySolver&;
auto getEnergySolver() const -> solver::EnergySolver const&;
It's unclear why the "traditional" method is not good for your purposes.
The machination with decltype and add/remove trickery is to cover ground for templates that want to be overly generic. Where you must deal with unknown types and ones you have no control over.
For the usual situations it is way clearer to just add a few typedefs and use those directly.
For your attempt, IMO you use it incorrectly, either try const auto& in front, or auto in front and assemble the type all the way after the ->, combininge remove_reference, then add_const then add_lvalue_reference, it may work, though would make head spin.
decltype(PhysicalNode::energySolver_) is plain EnergySolver, not const EnergySolver even though the method is const, since the expression doesn't use this, which is what that const qualifier really affects. And you're not allowed to use decltype(this->energySolver_) in this context since PhysicalNode is not yet complete.
You'll have to do something like -> const decltype(PhysicalNode::energySolver_)&; or -> std::add_lvalue_reference<std::add_const<decltype(PhysicalNode::energySolver_)>::type>::type; or something in between.

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