I have a vector that looks like this.
std::vector<std::pair<int,int> > v;
and I want to pass it into a function and make it so it's constant.
first I tried
void fun(const std::vector<std::pair<int,int> > v)
but that function allowed me to have the line
std::pair<int,int> p = v[i];
which I thought should have thrown an error since pair is not of type const. Then I realized that it's only the pointers that are being declared constant in vector so I tried
void fun(const std::vector<const std::pair<int,int> > v)
but that throws the error about there being no conversion.
I'm sure there's some inner workings that I'm not understanding that makes this illegal but would love some insight. Thanks.
I think you confuse reference and value semantics here. Lets the first function signature you tested:
void fun(const std::vector<std::pair<int,int> > v)
This copies the function argument into a new object v, which is additionaly const-qualified. Such signatures seldomly make sense: either you want to pass it as a const reference (to avoid copying) or you want to pass it by non-const value, because the function body will mutate the argument, but shall operate on its only copy. Besides, this snippet:
std::pair<int,int> p = v[i];
compiles just fine with the above function signature, because it copies the vector element at position i into a new pair object. The latter can be mutated, but this doesn't affect the vector at all.
Let's now consider the second function signature:
void fun(const std::vector<const std::pair<int,int> > v)
The same as before still applies here, and in addition, std::vector<const T> isn't useful, see this thread for an explanation.
How to fix it? If you don't want to copy the argument, but the function doesn't modify the vector, pass it by const reference. If the function modifies the vector and these modifications shall be visible at the call side, pass it as non-const reference. If the function modifies the vector but that shall happen independently of the call side vector, pass by non-const value.
void fun(const std::vector<std::pair<int,int> > v)
{
////
std::pair<int,int> p = v[i];
}
https://en.cppreference.com/w/cpp/container/vector/operator_at
No error will happen here because std::vector has overloaded operator[] both for const and non-const instances. In your case it will be resolved to const version and return const & to underlying i element.
Consider following code:
void fun(const std::vector<std::pair<int,int> > v)
{
////
std::pair<int,int> p = v[i]; //OK
v[i] = std::pair<int,int>{10,20}; //Compile error, trying to modify const data!
}
Related
I have a function with a template parameter T and would like to pass a value of type const T by reference.
The C++ compiler throws an error, (kind of) understandably so. Hence I was wondering if there exists a way to do this in a safe and concise way?
I created a very small example that reflects the issue I am having in my project.
(in my project the issue appears in a constant member function of some class, but from my experiments the issue should be "faithfully" reflected in the example below by use of a constant variable of int instead for simplicity's sake).
I am aware that I could theoretically use a separate template parameter "cT", but that would be horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument ...
I also understand that I could simply refrain from using templates at all and just specify this for every type.
I was just wondering if what I am trying to achieve below can be done with templates.
Thanks and have a nice day! :)
template<typename T>
bool ContainsElement(std::list<T>& findList, const T& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
int main()
{
std::list<int*> myList;
const int testConst = 6;
auto pointerToTestConst = &testConst;
ContainsElement(myList, pointerToTestConst); // compiler screams
}
The issue is in incompatibility between pointers:
pointerToTestConst is of type const int* - non-const pointer to const integer. Therefore T=const int*
myList is of type list<int*>, deducing T=int*.
Since those types are not the same, compilation fails. Rightfully so, because elem would allow changing testConst if T=int*.
The issue manifests regardles you passing elem by (const) reference or value.
horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument
So what? They get a compiler error about the comparison. It is no more unsafe than your code right now. I would argue that a generic ContainsElement should not care what you pass to is as long as it compares equal to some element in the list, it is a match.
Of course STL already offers this for std::find and std::ranges::find which also does not care and it is not called unsafe because of it.
To "normalize" the type for pointers and non-pointers, std::conditional may be used, finally your template still defines single T parameter:
#include <type_traits>
template<typename T>
bool ContainsElement(std::list<T>& findList,
const std::conditional_t<
std::is_pointer<T>::value,
std::add_pointer_t<
std::add_const_t<
std::remove_pointer_t<T>
>
>,
std::add_const_t<T>
>& elem)
{
for (auto& entry : findList)
{
if (entry == elem)
return true;
}
return false;
}
Above example uses helper types with postfix _t introduced in C++14, for C++11, use std::conditional::type.
It works for std::list<int> and std::list<int*>.
Of course in case of std::list<int*> it will check whether list contains exactly the same pointer address, not the pointed value.
So, I'm creating a class that represents an arbitrarily-sized matrix, and I want to write a function that "exports" the internal Vector to a 2-dimensional array.
I've found some code that seems to do what I want. Here is what the code looks in my class:
// Export matrix to array
double** Matrix::a_data() const
{
vector<double*> ptrs;
for (auto& vec : mat)
{
ptrs.push_back(vec.data());
}
return ptrs.data();
}
where mat is the vector < vector < double > > used to store the matrix's entries.
The post in question stated that vec needed to be an auto& instead of an auto because it is "very important to prevent vec from being a temporary copy."
However, in Visual Studio I'm always getting an error "no instance of overloaded function push_back matches the argument list... argument types are (const double*)"
So, my question is, how can I get this to work without changing auto& to auto? Also, how would one extend this to 3 dimensions?
You are running into a const problem.
Assuming mat is of type std::vector<std::vector<double>>, vec will be of type std::vector<double> const& in the function. The data() member function for const objects returns const*, not a regular pointer.
Hence, you need to use:
vector<double const*> ptrs;
But then, using
return ptrs.data();
is going to cause bigger problems. ptrs is function local variable. It is going to be destructed when the function returns. Hence, the returned pointer will be a dangling pointer.
Options to overcome that problem.
Change the return type to std::vector<double const*>.
Create a dynamic array of double const** and return it.
I strongly recommend the first option.
According to std::map documentation, it stores the key-value pairs in std::pair<const Key, Value>, so the keys in the map are const.
Now imagine that I have an std::map where the keys are the pointers to some objects.
struct S {};
struct Data {};
using MyMap = std::map<S*, Data>;
Let's also assume there is a function foo that accepts S* parameter.
void foo(S* ptr) { /* modify the object (*ptr) */ }
Now, the question is: when I iterate over MyMap with range-based for-loop, I am able to pass the map element key to foo:
MyMap m = getMyMapSomehow();
for (auto elem : m)
{
static_assert(std::is_const<decltype(elem.first)>::value, "Supposed to be `const S*`");
foo(elem.first); // why does it compile?
}
So, even though my static_assert succeeds (so I assume that the type of elem.first is const S*), the call to foo compiles fine, and therefore it look as if I'm able to modify the object behind pointer-to-const.
Why am I able to do that?
P.S. Here's a live example at Coliru that illustrates my point. For brevity, I use int instead of S and Data.
so I assume that the type of elem.first is const S*
No. The key stored in map is const, that means for std::map<S*, Data>, the key will be S* const (i.e. const pointer), not const S* (i.e. pointer to const). So it's fine to pass it to foo(S* ptr), the const pointer itself will be copied to the parameter.
Here is a simpler example, see if you can work it out:
void f(int); // takes a non-const int
int main() {
std::set<int> s; // elements are const ints
for (auto n : s) {
f(n); // OK!?
}
}
std::map<K, V>::value_type is std::pair<const K, V>, as you mentioned. So what is const K when S* is substituted for K? The answer, which might surprise you, is not const S*. but rather S* const.
The type vector<char *> is not convertible to const vector<const char*>. For example, the following gives a compilation error:
#include <vector>
using namespace std;
void fn(const vector<const char*> cvcc)
{
}
int main()
{
vector<char *> vc = vector<char *>();
fn(vc);
}
I understand why vector<char*> is not convertable to vector<const char*> - extra members of type const char * may be added to the vector, and afterwards they would be accessible as non-const. However, if the vector itself is const, this can't happen.
My best guess is that this would be harmless, but there is no way the compiler is allowed to deduce that this would be harmless.
How can this be worked around?
This question was suggested by the C++ FQA here.
In general, C++ does not allow you to cast someclass<T> to someclass<U> as a template someclass might be specialized for U. It doesn't matter how T and U are related. This mean that the implementation and thus the object layout might be different.
Sadly, there is no way to tell the compiler in which cases the layout and/or behaviour didn't change and the cast should be accepted. I imagine it could be very useful for std::shared_ptr and other use-cases, but that is not going to happen anytime soon (AFAIK).
void fn(const vector<const char*>)
As the top-level const qualifier is dropped for the function type, this is (at the call site) equivalent to:
void fn(vector<const char*>)
Both of which request a copy of the passed vector, because Standard Library containers follow value semantics.
You can either:
call it via fn({vc.begin(), vc.end()}), requesting an explicit conversion
change the signature to, e.g. void fn(vector<const char*> const&), i.e. taking a reference
If you can modify the signature of fn, you can follow GManNickG's advice and use iterators / a range instead:
#include <iostream>
template<typename ConstRaIt>
void fn(ConstRaIt begin, ConstRaIt end)
{
for(; begin != end; ++begin)
{
std::cout << *begin << std::endl;
}
}
#include <vector>
int main()
{
char arr[] = "hello world";
std::vector<char *> vc;
for(char& c : arr) vc.push_back(&c);
fn(begin(vc), end(vc));
}
This gives the beautiful output
hello world
ello world
llo world
lo world
o world
world
world
orld
rld
ld
d
The fundamental issue is to pass around Standard Library containers. If you only need constant access to the data, you don't need to know the actual container type and can use the template instead. This removes the coupling of fn to the type of container the caller uses.
As you have noticed, it's a bad idea to allow access of a std::vector<T*> through a std::vector<const T*>&. But if you don't need to modify the container, you can use a range instead.
If the function fn shall not or cannot be a template, you could still pass around ranges of const char* instead of vectors of const char. This will work with any container that guarantees contiguous storage, such as raw arrays, std::arrays, std::vectors and std::strings.
To work around the issue, you could implement your own template wrapper:
template <typename T> class const_ptr_vector;
template <typename T> class const_ptr_vector<T *> {
const std::vector<T *> &v_;
public:
const_ptr_vector (const std::vector<T *> &v) : v_(v) {}
typedef const T * value_type;
//...
value_type operator [] (int i) const { return v_[i]; }
//...
operator std::vector<const T *> () const {
return std::vector<const T *>(v_.begin(), v_.end());
}
//...
};
The idea is that the wrapper provides an interface to the referenced vector, but the return values are always const T *. Since the referenced vector is const, all the interfaces provided by the wrapper should be const as well, as illustrated by the [] operator. This includes the iterators that would be provided by the wrapper. The wrapper iterators would just contain an iterator to the referenced vector, but all operations that yield the pointer value would be a const T *.
The goal of this workaround is not to provide something that can be passed to a function wanting a const std::vector<const T *> &, but to provide a different type to use for the function that provides the type safety of such a vector.
void fn(const_ptr_vector<char *> cvcc)
{
}
While you could not pass this to a function expecting a const std::vector<const T *> &, you could implement a conversion operator that would return a copy of a std::vector<const T *> initialized with the pointer values from the underlying vector. This has also been illustrated in the above example.
As the FQA suggests, this is a fundamental flaw in C++.
It appears that you can do what you want by some explicit casting:
vector<char*> vc = vector<char *>();
vector<const char*>* vcc = reinterpret_cast<vector<const char*>*>(&vc);
fn(*vcc);
This invokes Undefined Behavior and is not guaranteed to work; however, I am almost certain it will work in gcc with strict aliasing turned off (-fno-strict-aliasing). In any case, this can only work as a temporary hack; you should just copy the vector to do what you want in a guaranteed manner.
std::copy(vc.begin(), vc.end(), std::back_inserter(vcc));
This is OK also from the performance perspective, because fn copies its parameter when it's called.
for_each accepts InputIterators :
//from c++ standard
template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function f);
is it ok to change the object in Function f, like this :
struct AddOne
{
void operator()(int & x){x = x + 1;}
};
std::vector<int> vec(10);
std::for_each(vec.begin(),vec.end(),AddOne());
This code works in VC++2008 and also with GCC, but is it also portable (legal) code ?
(InputIterators are only guaranteed to be usable as rvalue, in this case they are used as lvalue in AddOne's operator())
Read this article.
To be pedantic: for_each is a non-modifying sequence operation. The intent is not to modify the sequence. However, it is okay to modify the input sequence when using for_each.
You misunderstand something. Saying input iterators are only guaranteed to be usable as rvalues doesn't mean you can't get an lvalue out of an iterator somehow. So it does not mean that the result of *iterator is an rvalue. What you/for_each passes to AddOne is the result of operator* - not the iterator itself.
About for_each and a modifying function object - read this question
If in doubt use std::transform as it will convey even to the casual reader of your code that you intend to modify something.
It is legal - the input iterators are used to specify the range, not to do the processing.
Interestingly, Josuttis in his book "The C++ Standard Library" lists for_each as modifying, raher than non-modifying.
My suggestion would be, it all matters how you pass your item to the function i.e. whether by reference or pointer and change it or as a copy and change it.
But as other's have said, if you want to change the values, it's better to use transform as it cares about the return values.
class MultiplyBy {
private:
int factor;
public:
MultiplyBy(int x) : factor(x) {
}
int operator () (int other) const {
other = factor + other;
return other;
}
// int operator () (int & other) const {
// other = factor + other;
// return other;
// }
};
vector<int> x1= {1, 2, 3, 4, 5};
vector<int> x2;
std::transform(x1.begin(), x1.end(), back_inserter(x2), MultiplyBy(3));
std::for_each(x1.begin(), x1.end(), MultiplyBy(3));