why is it so useful to make a function const if you only can read variables but not write(class variable)?
If you pass something else a const pointer or const reference to an instance of your class then it can only call the class's const methods (if any).
Obviously, if you never bother with const-correctness with your types then you can ignore this.
I suppose it may also help the compiler optimize things in certain situations, although I am doubtful and, even if it did help, allowing that small improvement (if any) to dictate how you wrote your code would be a case of premature optimization in most situations.
So that you do not "accidentally" modify one of the class variables. It is just a safety measure.
(If you use the const keyword after a function that does modify any data member of the class - either directly or through another function call - you will get a compilation error).
One reason is that const is a virus. That means that if part of the code is const-correct, then the rest of the code won't interoperate with that part.
If you ignore const-correctness, chances of your classes working hand-in-hand with other libraries (beginning with the standard library) are slim.
For example:
#include <vector>
#include <algorithm>
struct X
{
int n;
bool operator< (X b)
{
return n < b.n;
}
};
int main()
{
std::vector<X> vec;
std::sort(vec.begin(), vec.end());
}
With codepad.org
In function 'const _Tp& std::__median(const _Tp&, const _Tp&, const _Tp&) [with _Tp = X]':
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2642: instantiated from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >, _Size = int]'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2713: instantiated from 'void std::sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >]'
t.cpp:17: instantiated from here
Line 90: error: no match for 'operator<' in '__a < __b'
A stdlib compatible comparison operator must make a promise that arguments are not modified. If objects actually were to change while they are compared, an attempt to sort them would be rather futile.
Another example: you won't be able to pass arguments by const reference which is the conventional way of passing large objects. Instead you'd have to pass arguments by modifiable references. Now you won't be able to pass temporaries of any kind to your functions.
If you have a const object, it only allows const member functions to operate on it.
The more of the contract between caller and callee you can express in code instead of documentation, the more help the compiler can give you with complying with that contract (from both ends). const-ness of the implicit this pointer is an important part of that as is const-ness of all other parameter referents. (of course top-level constness of pass-by-value parameters is not part of the contract)
The benefit is that you can get the compiler to enforce where state can be modified. For example if you make a class with private data and all its methods, except, say, the constructor, are const, then you have an immutable data type. The benefit of this is not one of performance, but one of semantics.
Related
I'm trying to implement a priority queue which uses an object which has a const member that is used to define the priority of objects in the queue. The following is a stripped down version of what I'm using
#include <vector>
#include <queue>
class Event {
public:
Event(float _time) : time(_time) {};
const float time;
};
struct EventComp {
public:
bool operator()(const Event& a, const Event& b) const {
return a.time < b.time;
}
};
class EventQueue {
private:
std::priority_queue<Event, std::vector<Event>, EventComp> events;
};
int main(int argc, char *argv[])
{
EventQueue q;
}
When I try to compile (using g++ -std=c++11), I get the following error messages, which I don't quite understand.
g++ -std=c++11 events.cpp -o events
In file included from /usr/include/c++/4.8/queue:62:0,
from events.cpp:2:
/usr/include/c++/4.8/bits/stl_heap.h: In instantiation of ‘void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Event*, std::vector<Event> >; _Distance = long int; _Tp = Event; _Compare = EventComp]’:
/usr/include/c++/4.8/bits/stl_heap.h:448:15: required from ‘void std::make_heap(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Event*, std::vector<Event> >; _Compare = EventComp]’
/usr/include/c++/4.8/bits/stl_queue.h:411:48: required from ‘std::priority_queue<_Tp, _Sequence, _Compare>::priority_queue(const _Compare&, _Sequence&&) [with _Tp = Event; _Sequence = std::vector<Event>; _Compare = EventComp]’
events.cpp:15:7: required from here
/usr/include/c++/4.8/bits/stl_heap.h:315:29: error: use of deleted function ‘Event& Event::operator=(Event&&)’
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild));
^
events.cpp:4:7: note: ‘Event& Event::operator=(Event&&)’ is implicitly deleted because the default definition would be ill-formed:
class Event {
^
events.cpp:4:7: error: non-static const member ‘const float Event::time’, can’t use default assignment operator
In file included from /usr/include/c++/4.8/queue:62:0,
from events.cpp:2:
/usr/include/c++/4.8/bits/stl_heap.h:321:29: error: use of deleted function ‘Event& Event::operator=(Event&&)’
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first
^
I assume that some part of the internal structure of the priority_queue needs the move assignment operator to insert elements, but the const member seems to prevent that operator from being defined. I tried using the rule of five, but it didn't seem to work out. What do I need to add to the class definitions to get this working?
edit: I'm aware that I can remove the const qualifier from the member variable, make it private, and add an accessor member function to get the value of the variable, but I would rather have it public and const, so I'm interested in solutions which keep the member variable as it is in the example (if that is even possible).
The default constructor of std::priority_queue<Event, ...> requires Event to be MoveAssignable because it calls std::make_heap (which has this requirement). Event is not MoveAssignable because of the const float time data member. If you remove the const qualifier from this data member, your code should compile.
I'm aware that I can remove the const qualifier from the member
variable, make it private, and add an accessor member function to
get the value of the variable, but I would rather have it public and
const, so I'm interested in solutions which keep the member variable
as it is in the example (if that is even possible).
Ultimately, to use this std::lib facility, Event will have to be MoveAssignable. This requirement can be met with either a move assignment operator, or a copy assignment operator. Either or both of these can be supplied by you, or the compiler. But when you have a const data member, the compiler supplied special member will be implicitly deleted.
Go ahead and supply your own copy and/or move assignment operator if you desire:
class Event {
public:
Event(float _time) : time(_time) {};
const float time;
Event(Event&&) = default;
Event& operator=(Event&&); // Supply this
};
I do not have a good guess as to what you might want the move (or copy) assignment operator to do, since you have stated that once constructed, you do not want time to ever change. That is a design decision which you must work out (and has more to do with software design in general, than C++ syntax).
<aside>
In the comments of the question POW notes that the code in the question compiles with clang (more accurately, with libc++). The reason for this is a nearly conforming extension in libc++:
The standard specifies this priority_queue constructor:
explicit priority_queue(const Compare& x = Compare(),
Container&& c = Container());
which calls std::make_heap with the container c. But the standard also gives leeway to implementors to replace member function signatures with default values, with equivalent overloads. libc++ replaces this single signature with the following three:
priority_queue();
explicit priority_queue(const Compare& x);
explicit priority_queue(const Compare& x, Container&& c);
And only the 3rd needs to call std::make_heap. Thus the default constructor in libc++ places fewer requirements on the value_type.
I said nearly conforming. The part that is not completely conforming is that the default constructor is not declared explicit. This was a conscious design decision on the part of the implementor to make priority_queue easier to use.
</aside>
I have this following function to test out thrust but it doesn't compile. It appears my predicate is not valid and my knowledge of C/C++ is not strong enough to know what I need to do correct the compilation issue. Please could someone explain what's going on here and how I can fix it.
void filter(device_vector<int>& x,
device_vector<int>& y,
thrust::unary_function<int,bool> f) {
thrust::copy_if(x.begin(), x.end(), y.end(), f);
}
nvcc -o test test.cu -O2
/usr/lib/nvidia-cuda-toolkit/include/thrust/detail/internal_functional.h(102): error: call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type
detected during:
instantiation of "__nv_bool thrust::detail::predicate_to_integral<Predicate, IntegralType>::operator()(const T &) [with Predicate=thrust::unary_function<__nv_bool, int>, IntegralType=std::ptrdiff_t, T=int]"
/usr/lib/nvidia-cuda-toolkit/include/thrust/detail/function.h(187): here
instantiation of "Result thrust::detail::host_device_function<Function, Result>::operator()(const Argument &) const [with Function=thrust::detail::predicate_to_integral<thrust::unary_function<__nv_bool, int>, std::ptrdiff_t>, Result=std::ptrdiff_t, Argument=thrust::device_reference<int>]"
/usr/lib/nvidia-cuda-toolkit/include/thrust/iterator/transform_iterator.h(306): here
instantiation of "thrust::detail::transform_iterator_base<AdaptableUnaryFunction, Iterator, Reference, Value>::type::reference thrust::transform_iterator<AdaptableUnaryFunction, Iterator, Reference, Value>::dereference() const [with AdaptableUnaryFunction=thrust::detail::predicate_to_integral<thrust::unary_function<__nv_bool, int>, std::ptrdiff_t>, Iterator=thrust::detail::normal_iterator<thrust::device_ptr<int>>, Reference=std::ptrdiff_t, Value=thrust::use_default]"
/usr/lib/nvidia-cuda-toolkit/include/thrust/iterator/iterator_facade.h(128): here
instantiation of "Facade::reference thrust::iterator_core_access::dereference(const Facade &) [with Facade=thrust::transform_iterator<thrust::detail::predicate_to_integral<thrust::unary_function<__nv_bool, int>, std::ptrdiff_t>, thrust::detail::normal_iterator<thrust::device_ptr<int>>, std::ptrdiff_t, thrust::use_default>]"
/usr/lib/nvidia-cuda-toolkit/include/thrust/iterator/iterator_facade.h(305): here
Let's look at some reference first:
The function object pred shall not apply any non-constant function
through the dereferenced iterator. This function object may be a
pointer to function or an object of a type with an appropriate
function call operator.
From "Predicate".
Thus, you shouldn't limit the choices of the caller to unary_function. In a case like this, any object, function, whatever that can be used in the context of copy_if should be allowed to pass. Actually, in vanilla C++ unary_function is deprecated (in favor of std::function, which shouldn't be used here anyway for exactly the same reasons).
What's the difference between the following two cases?
std::pair<int,std::string> example_1 (std::make_pair (1,"foo"));
int value_1 = boost::bind (&std::pair<int,std::string>::first,_1) (example_1);
std::map<int,std::string>::value_type example_2 (std::make_pair (2,"boo"));
int value_2 = boost::bind (&std::pair<int,std::string>::first,_1) (example_2);
The first example works fine but the second does not compile when the binding is done. I have looked at the file stl_map.h and value_type is defined as follows:
typedef std::pair<const _Key, _Tp> value_type;
I don't see the difference. I would appreciate is someone can tell let me know and the reason why the second example does not compile.
The compilation error message is:
.../include/boost/bind/mem_fn.hpp:333:36: error: no matching function for call to ‘get_pointer(const std::pair<const int, std::basic_string<char> >&)’
make: *** [main.o] Error 1
Thanks in advance!
The difference is the use of const in the map value type. std::pair<int,std::string>::first is not an accessible item on std::pair<const int,std::string>::first. Yes, there's an implicit conversion defined by pair from the const version to the non-const version, but that conversion isn't considered for the purposes of calling a member function like this. Those uses of pair might look similar, but really they are completely independent classes that are unrelated to each other in terms of where field locations and such might go.
In essence, you'e asked boost to build code like
std::pair<const int,std::string> example(3, "Hello");
example.std::pair<int,std::string>::first
which is not valid.
map's value_type has a const key (const int in your case), whereas the pair you're using doesn't (plain int in your case).
In the past I have used both templates and dynamic binding in C++, however recently I attempted to use them together and found that it was impossible to compile.
What I am trying to do is something like this:
std::map<MyClass, unsigned int> mymap;
Where MyClass happens to be a class utilizing dynamic memory binding. After a few hours of searching I am given the impression that this causes implications which I still can't resolve, so I was hoping for some guidance on the issue and how it can be resolved.
My final goal is to be able to do something like this:
std::vector<MyClass> MyClassPool;
//fill the vector with MyClass objects...
for(usigned int i=0 ; i<MyClassPool.size() ; i++)
{
mymap[ MyClassPool[i] ] = i;
}
I have tried using pointers in various ways but it's not working and I fail to see what can be done.
I get the following errors with the above:
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h: In member function `bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Instance]':
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_map.h:338: instantiated from `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = Instance, _Tp = float, _Compare = std::less<Instance>, _Alloc = std::allocator<std::pair<const Instance, float> >]'
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h:227: error: no match for 'operator<' in '__x < __y'
That compile error means you have no operator < defined for Instance. map needs to be able to sort keys and needs this function. If you'd rather not define operator <, you need to provide a comparison function as a third template parameter for map, i.e. std::map<Instance, float, compare_instances>.
... Come to think of it, you're sure you want Instance as the key and float as the data, and not the other way around? I.e. you're searching in the map for an Instance to get a float in return?
You don't provide operator< for MyClass. It is required by std::map. You have two options: provide a comparator as the third template argument to map OR implement the operator in MyClass.
This has nothing to do with "dynamic binding" (which is not what is meant here anyway). Your class needs to have an order to be put into a map. It needs operator<.
I have a class I've written, meant to represent vectors (in the linear algebra sense). I just started writing it so it isn't finished, but i've pasted it and some test code here
I'm not really sure what's going on. I was going to write an overloaded operator<< for testing in a second, so I went ahead and put it in my main function (so I could use compiler errors to make sure I'd written it properly).
What does this warning mean? why it is looking at the address of v? I tried removing the parentheses from v, and I end up with this, a bunch of horrible template errors:
`
In function 'typename boost::range_const_iterator<C>::type boost::range_detail::boost_range_begin(const C&) [with C = vect<float, 3u>]':
/usr/local/include/boost/range/begin.hpp:164: instantiated from 'typename boost::range_const_iterator<C>::type boost::begin(const T&) [with T = vect<float, 3u>]'
prelude/more_stdlib_ostreaming.hpp:64: instantiated from 'void more_stdlib_ostreaming_detail::print_range(std::basic_ostream<_CharT, _Traits>&, const Range&) [with C = char, Tr = std::char_traits<char>, Range = vect<float, 3u>]'
prelude/more_stdlib_ostreaming.hpp:76: instantiated from 'typename more_stdlib_ostreaming_detail::snd<typename R::iterator, std::basic_ostream<_CharT, _Traits>&>::type operator<<(std::basic_ostream<_CharT, _Traits>&, const R&) [with C = char, Tr = std::char_traits<char>, R = vect3f]'
t.cpp:42: instantiated from here
Line 45: error: 'const class vect<float, 3u>' has no member named 'begin'`
I can sort of see whats going on here, tho. It looks like it is somehow using boost's range for and trying to iterate over my container, and it is failing because I haven't defined begin() and end(). The same thing happens if I instantiate v using v(some_float) rather than without the parens.
So, two questions:
Why is v() behaving differently than v? I thought that declaring a object without parens always calls the default ctor anyway, and explicitly calling the default ctor made no difference?
What is codepad's compiler (gcc 4.1.2) doing here? Does it have a template that automatically tries to generate an appropriate operator<
Also, feel free to tell me anything else I'm doing stupid/wrong/bad style here (besides rolling my own matrix math library for fun, which I know is unnecessary. I'm doing it as an exercise)
First of all, vect3f v(); declares a function (named v), taking no parameters and returning vect3f. And the operator<< that is being called is called for this function pointer (which is implicitly converted to bool, because there's no overload for function pointers).
vect3f v; is the correct way of creating default-constructed object.
And no, compiler won't try to generate ostream& operator<<(ostream& /* ... */) for you. There are however many overloads for all fundamental types and even some other types (such as std::string).
You can check all basic overloads here.