Using BOOST_FOREACH with a constant intrusive list - c++

Consider the following code to iterate over an intrusive list using the BOOST_FOREACH macro:
#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>
typedef boost::intrusive::list<
boost::intrusive::list_base_hook<> > MyList;
void iterate (const MyList& xs) {
BOOST_FOREACH (MyList::const_reference node, xs);
}
int main () {
MyList xs;
iterate (xs);
return 0;
}
Given boost version 1.48 the code fails with clang 3.2 (SVN) and gcc 4.6.3, but works with gcc 4.5.3. With non-const-qualified parameter xs to iterate the code works. With C++11 enabled all of the compilers accept the code. When using boost-1.46 both gcc versions accept the code, but clang still doesn't.
Is the code at hand a misuse of the BOOST_FOREACH macro, or is the error at boosts side? Is there a workaround that is nicer than iteration with regular for-loop?
Edit:
I pasted the error messages to pastebin (both are very verbose) for GCC and clang.

Here is what I could gather from the logs as well as my deductions as to the cause of failure.
Short version: for some reason BOOST_FOREACH attempts to copy the data which is not possible.
There is a note on the Extensibility page:
Making BOOST_FOREACH Work with Non-Copyable Sequence Types
For sequence types that are non-copyable, we will need to tell BOOST_FOREACH to not try to make copies. If our type inherits from boost::noncopyable, no further action is required. If not, we must specialize the boost::foreach::is_noncopyable<> template [...] Another way to achieve the same effect is to override the global boost_foreach_is_noncopyable() function. Doing it this way has the advantage of being portable to older compilers.
From the diagnosis, it is unclear whether the type is properly configured, so you might want to give it a go.
Pruned diagnosis and analysis.
/usr/include/boost/foreach.hpp:571:37: error: no matching constructor for initialization of 'boost::intrusive::list< >'
::new(this->data.address()) T(t);
^ ~
/usr/include/boost/foreach.hpp:648:51: note: in instantiation of member function 'boost::foreach_detail_::simple_variant<boost::intrusive::list< > >::simple_variant' requested here
return auto_any<simple_variant<T> >(*rvalue ? simple_variant<T>(t) : simple_variant<T>(&t));
^
/usr/include/boost/intrusive/list.hpp:1490:35: note: candidate constructor not viable: 1st argument ('const boost::intrusive::list< >') would lose const qualifier
BOOST_MOVABLE_BUT_NOT_COPYABLE(list)
^
/usr/include/boost/move/move.hpp:371:7: note: expanded from macro 'BOOST_MOVABLE_BUT_NOT_COPYABLE'
TYPE(TYPE &);\
/usr/include/boost/intrusive/list.hpp:1497:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to 'const value_traits' (aka 'const boost::intrusive::detail::base_hook_traits<boost::intrusive::list_base_hook< >, boost::intrusive::list_node_traits<void *>, 1, boost::intrusive::default_tag, 1>') for 1st argument;
list(const value_traits &v_traits = value_traits())
^
/usr/include/boost/intrusive/list.hpp:1506:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to '::boost::rv<list< >> &' for 1st argument;
list(BOOST_RV_REF(list) x)
^
/usr/include/boost/intrusive/list.hpp:1502:4: note: candidate constructor template not viable: requires at least 2 arguments, but 1 was provided
list(Iterator b, Iterator e, const value_traits &v_traits = value_traits())
^
I tried to isolate the error as much as possible (removing backtraces etc..) Apparently the problem stems from boost::intrusive::list, and more precisely the inability to build a new boost::intrusive::list<> from a boost::intrusive::list<> const.
The most promising constructor is defined by a macro:
BOOST_MOVABLE_BUT_NOT_COPYABLE(list)
which expands to
list(list&);
which is the way boost emulates move semantics for non-copyable types in C++03. However it cannot move from a const item since the const qualifier would be lost.
This looks to be part of the trickery used by BOOST_FOREACH to avoid multiple evaluation of the container argument (in case it is a function invocation) though I am a little surprised it tries to copy the argument here.

Since you are using gcc > 4.6 and clang 3.2, you could use C++11's range-based for looops:
#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>
typedef boost::intrusive::list<
boost::intrusive::list_base_hook<> > MyList;
void iterate (const MyList& xs) {
for(const auto &node : xs) {
// do something with node
}
}
int main () {
MyList xs;
iterate (xs);
return 0;
}
You could also use std::for_each:
void iterate (const MyList& xs) {
std::for_each(xs.begin(), xs.end(),
[](MyList::const_reference node) {
// do something with node
}
);
}

Related

How to brace initialize a std::stack in C++? [duplicate]

Program 1:
#include <iostream>
#include <cstdlib>
#include <vector>
int main(){
//compiles successfully
std::vector<int> vec{1,2,3,4,5};
return EXIT_SUCCESS;
}
Program 2:
#include <iostream>
#include <cstdlib>
#include <queue>
int main(){
//compiler error
std::queue<int> que{1,2,3,4,5};
return EXIT_SUCCESS;
}
Error message:
main.cpp: In function ‘int main()’:
main.cpp:7:31: error: no matching function for call to ‘std::queue<int>::queue(<brace-enclosed initializer list>)’
main.cpp:7:31: note: candidates are:
/usr/include/c++/4.6/bits/stl_queue.h:141:7: note: std::queue<_Tp, _Sequence>::queue(_Sequence&&) [with _Tp = int, _Sequence = std::deque<int, std::allocator<int> >]
/usr/include/c++/4.6/bits/stl_queue.h:141:7: note: candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:137:7: note: std::queue<_Tp, _Sequence>::queue(const _Sequence&) [with _Tp = int, _Sequence = std::deque<int, std::allocator<int> >]
/usr/include/c++/4.6/bits/stl_queue.h:137:7: note: candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: std::queue<int>::queue(const std::queue<int>&)
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: candidate expects 1 argument, 5 provided
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: std::queue<int>::queue(std::queue<int>&&)
/usr/include/c++/4.6/bits/stl_queue.h:92:11: note: candidate expects 1 argument, 5 provided
Question:
why can't queues be initialized like vectors?
I suppose they aren't sequence containers, but why would that matter?
I'm sure there is a good reason, but I can't find any explanations.
gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
queue<int> q({1, 2, 3});
I don't think it really has anything to do with being container adapters rather than containers (though I'll admit I'm uncertain exactly why the correct constructor is omitted).
When you use a braced initializer list with std::vector, you're using this (new in C++11) constructor:
vector(initializer_list<T>, const Allocator& = Allocator());
Looking at the definition of std::queue, the available constructors are:
explicit queue(const Container&);
explicit queue(Container&& = Container());
template <class Alloc> explicit queue(const Alloc&);
template <class Alloc> queue(const Container&, const Alloc&);
template <class Alloc> queue(Container&&, const Alloc&);
template <class Alloc> queue(const queue&, const Alloc&);
template <class Alloc> queue(queue&&, const Alloc&);
A constructor taking an initialization_list is conspicuously absent.
I'm quite certain that despite being a container adapter, such a constructor would be trivial if it was desired. Just for example:
#include <deque>
#include <initializer_list>
#include <iostream>
template <class T, class container=std::deque<T> >
class myqueue {
container data;
public:
explicit myqueue(std::initializer_list<T> t) : data(t) {}
void pop() { data.pop_front(); }
T front() const { return data.front(); }
bool empty() const { return data.empty(); }
};
int main(){
myqueue<int> data {1, 2, 3, 4};
while (!data.empty()) {
std::cout << data.front() << "\n";
data.pop();
}
return 0;
}
g++ 4.7 accepts this without any problems, and produces exactly the output you'd expect:
1
2
3
4
Although I haven't tested with any other compilers, I can't see any reason other compilers wouldn't work fine with this as well (provided they implement the necessary features, of course).
Edit: I just did some looking through the committee papers proposing the addition of initalizer_lists to C++ (e.g., N1890, N1919, N2100, N2215, N2220) and it looks to me like a simple oversight. Many of the earlier papers are more conceptual, but N2220 has a fair amount of proposed language for the working paper. For std::array (for one example) it specifically points out that no change is needed. It then goes through deque, vector, [unordered_][multi_](set|map), and shows changes needed for each -- but no mention is made of stack or queue at all, in either direction. No proposal to add support for std::initializer_list, nor (like std::array) reasoning for their omission.
I'd conclude that it was a simple oversight, that probably slipped through for two reasons: 1) the adapters are almost, but not quite containers, and 2) the adapter classes don't seem to be used a whole lot, so forgetting about them was probably fairly easy (and, of course, the ever-pervasive third reason: most of the active committee members are horribly overworked).
Edit2: I should probably add one more detail: since stack and queue can both accept another container for the initialization, you can pretty easily do something like:
std::stack<int> data(std::vector<int>{1,2,3,4});
This is somewhat verbose, but unlikely to cause any loss of efficiency (the container will be passed as an rvalue reference, so its representation will be "stolen" instead of copied). There is one caveat though: if the type of container you use doesn't match the container underlying the container adapter, you'll get a copy rather than a move (and consequently, may lose some efficiency).
std::queue and std::stack are not actually containers, they are so called container adaptors which uses a container (by default std::deque). Therefore you can not initialize it as other containers.
Edit
For a container to be able to use an initializer list, it must have a constructor taking an std::initializer_list as argument. The container adaptors don't do that. If it's deliberate or an oversight of the standards committee is up to anyones interpretation.

Undefined reference for ~queue with explicit template instantiation with Clang 10

The following code doesn't link Clang 10 but succeed with GCC and Clang 9:
#include <queue>
template <typename T>
class A
{
public:
void f();
private:
std::queue<int> q;
};
template <typename T>
void A<T>::f()
{
q = {};
}
template class A<int>;
int main()
{
return 0;
}
What I get from the compiler is:
Online example
/opt/compiler-explorer/gcc-9.3.0/lib/gcc/x86_64-linux-gnu/9.3.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/example-f70f65.o: in function `A<int>::f()':
/home/ce/<source>:16: undefined reference to `std::queue<int, std::deque<int, std::allocator<int> > >::~queue()'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
Compiler returned: 1
It works if I replace std::queue with std::vector, std::deque or std::set; or if I remove the explicit template instantiation.
It also works if I remplace q = {} with the full constructor call q = std::queue<int>{}.
Is this code not standard or is it a compiler/libc++ bug?
I am not sure why you get such linker error, maybe its some unique issue with godbolt. If you try to compile your code with coliru : https://coliru.stacked-crooked.com/a/ac9c188334f858d8 you will get a compiliation time error indicating that you try to use list initialization of your queue:
main.cpp:16:7: error: no viable overloaded '='
q = {};
~ ^ ~~
main.cpp:19:16: note: in instantiation of member function 'A<int>::f' requested here
template class A<int>;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/bits/stl_queue.h:96:11: note: candidate function (the implicit move assignment operator) not viable: cannot convert initializer list argument to 'std::queue<int, std::deque<int, std::allocator<int> > >'
class queue
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/bits/stl_queue.h:96:11: note: candidate function (the implicit copy assignment operator) not viable: cannot convert initializer list argument to 'const std::queue<int, std::deque<int, std::allocator<int> > >'
1 error generated.
queue does not allow list initialization with initializer_list, there is SO on this here: Why can't I construct a queue/stack with brace-enclosed initializer lists? (C++11)
But it appears you can make your code compile (at least on coliru, I tried on godbolt with no success) if you use libc++ (-stdlib=libc++) : https://coliru.stacked-crooked.com/a/df9d859a239843cf
It might not give exact answer to your question, but I it was too long for a comment. Also you might find similar thread here : https://github.com/envoyproxy/envoy/issues/9106
[edit]
Interesting, after reseting godbolt UI and entering code again with the same configuration (https://godbolt.org/z/TzE9h9) everything works ok.

Stack Template Arguments

Focus on the template arguments
I can create a stack (an adapter class template from standard library) object like this,
stack<int, vector<int>> myStack;
I know the second template argument means the underlying data structure of the stack. But why the following line doesn't give a compile time error?
stack<int, vector<string>> myStack;
Notice that I'm declaring a stack to contain elements of type int, but at the same time I'm declaring the underlying data structure to hold string elements.
Functionally, It works as if it was a stack of string elements.
What you are doing is undefined behaviour. Nevertheless, I am going to explain why it seems to work fine.
The container adapter std::stack<T, TContainer> contains several type symbols that are aliases for types that will be commonly used. There is a list here.
One that concerns us here is std::stack::value_type. It basically determines what type the methods std::stack::push and friends expect:
void push( const value_type& value );
We can also see how it is defined:
using value_type = typename TContainer::value_type
So, the type that is used in all actions is actually only based on the second type, TContainer ! In your case, that is vector<string>::value_type, so value_type will be an alias to string. The type used for T, int in your case, is not used.
Thus, everything seems to work.
But even though this works in your case with your particular compiler, it is actually not allowed:
The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
You can find the source for this quote here.
From the documentation:
T - The type of the stored elements. The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
Undefined behaviour means it might compile, it might even work, or it might wipe your hard drive.
How it fails, if it does, is implementation dependant.
If I had to guess, I would imagine that your implementation is discarding the int template argument, and just uses Container::value_type instead.
Once we get to C++17, this will be explicitly illegal. Some compilers will already not appreciate this code..
For example:
#include <stack>
#include <string>
#include <vector>
int main() {
std::stack<int, std::vector<std::string>> s;
s.push(3);
}
Fails to compile under OS X (clang, libc++, c++11) with:
blah.cc:7:7: error: no matching member function for call to 'push'
s.push(3);
~~^~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:194:10: note:
candidate function not viable: no known conversion from 'int' to 'const value_type' (aka
'const std::__1::basic_string<char>') for 1st argument
void push(const value_type& __v) {c.push_back(__v);}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:197:10: note:
candidate function not viable: no known conversion from 'int' to 'value_type' (aka 'std::__1::basic_string<char>') for
1st argument
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
^
1 error generated.

C++ default copy/move assignment operator for extern C structure not const

I have a std::map<CXCursor, DeclarationContent> from which I want to remove elements using std::remove_if.
CXCursor is a (typedef of a) struct in external C code (libClang) that I cannot / must not modify.
Error messages from clang++ 3.4.2 --std=c++11 :
stl_pair.h:170:8: error: no viable overloaded '='
stl_algo.h:1152:23: note: in instantiation of member function std::pair<const CXCursor, DeclarationContent>::operator=' requested here
extract.cc:34:8: in instantiation of function template specialization 'std::remove_if<std::_Rb_tree_iterator<std::pair<const CXCursor, DeclarationContent> >, bool (*)(std::pair<const CXCursor, DeclarationContent> &)>' requested here
std::remove_if(decls.begin(), decls.end(), noCodeGenerationRequested);
include/clang-c/Index.h:2137:9: note: candidate function (the implicit copy assignment operator) not viable:
'this' argument has type 'const CXCursor', but method is not marked const
/include/clang-c/Index.h:2137:9: note: candidate function (the implicit move assignment operator) not viable:
'this' argument has type 'const CXCursor', but method is not marked const
The code is basically:
std::map<CXCursor, DeclarationContent> decls;
// filling the map
std::remove_if(decls.begin(), decls.end(), noCodeGenerationRequested);
with
bool noCodeGenerationRequested(std::map<CXCursor, DeclarationContent>::value_type & v)
{ /* FUN GOES HERE */ return true; }
From reading the error message it seems that the implicit assignment operators are not const qualified, which is needed in case of a map (as it's key is always const).
I could write a wrapper class around CXCursor that provides those assignment operators, but maybe there is another way?
std::remove_if does not work with a std::map, see: remove_if equivalent for std::map
map is not usable with std::remove_if, since map::value_type has type std::pair<const T, U>, but std::remove_if requires, that dereferenced iterator should be MoveAssignable. Use just loop, or probably copy_if in another container with negation of your predicate.
Or you can use boost::range::remove, if you already use boost in your project.

call of overloaded <brace-enclosed initializer list> is ambiguous, how to deal with that?

I really don't understand this, I thought that compiler first executes what is in braces and then gives the result to the most appropriate function. Here it looks like it gives the function an initializer list to deal with it...
#include <string>
#include <vector>
using namespace std;
void func(vector<string> v) { }
void func(vector<wstring> v) { }
int main() {
func({"apple", "banana"});
}
error:
<stdin>: In function 'int main()':
<stdin>:11:27: error: call of overloaded 'func(<brace-enclosed initializer list>)' is ambiguous
<stdin>:11:27: note: candidates are:
<stdin>:6:6: note: void func(std::vector<std::basic_string<char> >)
<stdin>:8:6: note: void func(std::vector<std::basic_string<wchar_t> >)
Why isn't my func(vector<string> v) overload called, and can I make it so?
This one was subtle.
std::vector has a constructor taking two range iterators. It is a template constructor (defined in 23.6.6.2 of the C++11 Standard):
template<typename InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& a = allocator_type());
Now the constuctor of std::vector<wstring> accepting an initializer_list is not a match for the implicit conversion in your function call, (const char* and string are different types); but the one above, which is of course included both in std::vector<string> and in std::vector<wstring>, is a potentially perfect match, because InputIterator can be deduced to be const char*. Unless some SFINAE technique is used to check whether the deduced template argument does indeed satisfy the InputIterator concept for the vector's underlying type, which is not our case, this constructor is viable.
But then again, both std::vector<string> and std::vector<wstring> have a viable constructor which realizes the conversion from the braced initializer list: hence, the ambiguity.
So the problem is in the fact that although "apple" and "banana" are not really iterators(*), they end up being seen as such. Adding one argument "joe" to the function call fixes the problem by disambiguating the call, because that forces the compiler to rule out the range-based constructors and choose the only viable conversion (initializer_list<wstring> is not viable because const char* cannot be converted to wstring).
*Actually, they are pointers to const char, so they could even be seen as constant iterators for characters, but definitely not for strings, as our template constructor is willing to think.