Why isn't this code compiling ?
#include <cstdlib>
#include <list>
template < typename Type >
class Allocator {
public:
using value_type = Type;
public:
template < typename Other >
struct rebind { using other = Allocator< Other >; };
public:
Type * allocate( std::size_t n ) { return std::malloc( n ); }
void deallocate( Type * p, std::size_t ) throw ( ) { std::free( p ); }
};
int main( void ) {
std::list< void *, Allocator< void * > > list;
return 0;
}
It seems to need pointer, reference, pointer_const & reference_const types. However, according to cppreference these members are all optionals. It seems like if the STL weren't using allocator_trait (I'm compiling with -std=c++11 so it should be good).
Any idea ?
[edit] On clang, errors are :
user#/tmp > clang++ -std=c++11 test.cc
In file included from test.cc:2:
In file included from /usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/list:63:
/usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/bits/stl_list.h:449:40: error: no type named 'pointer' in 'Allocator<void *>'
typedef typename _Tp_alloc_type::pointer pointer;
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
test.cc:17:46: note: in instantiation of template class 'std::list<void *, Allocator<void *> >' requested here
std::list< void *, Allocator< void * > > list;
^
In file included from test.cc:2:
In file included from /usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/list:63:
/usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/bits/stl_list.h:450:40: error: no type named 'const_pointer' in 'Allocator<void *>'
typedef typename _Tp_alloc_type::const_pointer const_pointer;
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
/usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/bits/stl_list.h:451:40: error: no type named 'reference' in 'Allocator<void *>'
typedef typename _Tp_alloc_type::reference reference;
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/bits/stl_list.h:452:40: error: no type named 'const_reference' in 'Allocator<void *>'
typedef typename _Tp_alloc_type::const_reference const_reference;
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
4 errors generated.
This is a bug in GCC's C++ standard library.
When using a list, they are not properly wrapping access to the allocator through an allocator_traits.
However, they do implement vector correctly. This code would compile if you used std::vector instead of std::list.
Related
I was trying to separate a "generic" map from the event manager implementation, because I need to use it somewhere else. But I ran into something quite unusual for me. So it seems like I'm trying to generate two times (at least) the GetValue function.
#include <tuple>
#include <utility>
#include <vector>
#include <functional>
namespace meta {
template < typename T >
struct CType { using type = T; };
}
namespace containers {
template < template < typename T > class T_Storage,
typename... T_Keys >
class TypedMap {
template < typename T_Key >
using U_Pair = decltype(
std::make_pair(
meta::CType<T_Key>{},
T_Storage<T_Key>{}
));
using U_Map = decltype(
std::make_tuple(
U_Pair<T_Keys>{}
...));
public:
U_Map m_map;
//-----------------------------------------
//! Functions
public:
//!
//! #fn GetValue
//! #brief Acess to map[key]
//! #param type is the key used to find the data
//! #return map[key] reference
//!
template < typename T_Key >
constexpr decltype(auto) GetPair() {
return std::get<U_Pair<T_Key>>(m_map);
}
template < typename T_Key >
decltype(auto) GetValue() {
return std::get<1>(GetPair<T_Key>());
}
};
}
//!
//! #class EventManager
//!
template < typename ... T_Events >
class EventManager {
template < typename T_Event >
using U_EventCallback = std::function<void(T_Event)>;
template < typename T_Event >
using U_ListenersArray = std::vector<U_EventCallback<T_Event>>;
private:
containers::TypedMap<U_ListenersArray> m_listenersMap;
public:
EventManager() = default;
~EventManager() = default;
public:
template < typename T_Event >
decltype(auto) GetListeners() {
return m_listenersMap.template GetValue<T_Event>();
}
};
struct event1 {};
struct event2 {};
using U_EventManager = EventManager<event1,event2>;
int main() {
U_EventManager test;
auto result = test.GetListeners<event1>();
}
Wandbox
UPDATE:
So the error was generated because of a typo... But still I'd like to understand how this error occured, to be able to understand it next time.
In file included from prog.cc:1: /opt/wandbox/clang-head/include/c++/v1/tuple:1018:5: error: static_assert failed due to requirement '!is_same<pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > >
>, pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > > > >::value' "type not in empty type list"
static_assert(!is_same<_T1, _T1>::value, "type not in empty type list");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/wandbox/clang-head/include/c++/v1/tuple:1025:14: note: in instantiation of template class 'std::__1::__find_detail::__find_exactly_one_checked<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
: public __find_detail::__find_exactly_one_checked<_T1, _Args...> {
^ /opt/wandbox/clang-head/include/c++/v1/tuple:1032:23: note: in instantiation of template class 'std::__1::__find_exactly_one_t<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return _VSTD::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
^ prog.cc:45:29: note: in instantiation of function template specialization 'std::__1::get<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return std::get<U_Pair<T_Key>>(m_map);
^ prog.cc:51:36: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetPair<event1>' requested here
return std::get<1>(GetPair<T_Key>());
^ prog.cc:79:40: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetValue<event1>' requested here
return m_listenersMap.template GetValue<T_Event>();
^ prog.cc:92:24: note: in instantiation of function template specialization 'EventManager<event1, event2>::GetListeners<event1>' requested here
auto result = test.GetListeners<event1>();
^ 1 error generated.
You forgot to provide the content for your containers:
template < typename ... T_Events >
class EventManager {
/* ... */
private:
containers::TypedMap<U_ListenersArray> m_listenersMap; // <-- this line, you forgot the type list.
/* ... */
};
The marked line should be:
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;
Not sure... but it seems to me you've forgotten to use T_Events... in EventManager.
I mean... instead of
containers::TypedMap<U_ListenersArray> m_listenersMap;
you should, I suppose, write
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;
I have the following snippet of code:
#include <algorithm>
#include <memory>
#include <vector>
// Example allocator, doesn't do anything but implements std::allocator_traits
template<typename T>
struct null_allocator {
using value_type = T;
using size_type = std::size_t;
using pointer = T *;
using const_pointer = const pointer;
//using difference_type = typename std::pointer_traits<pointer>::difference_type;
using reference = T &;
using const_reference = const T &;
null_allocator() {}
template<typename U>
null_allocator(const null_allocator<U>&) {}
T* allocate(std::size_t size) {
(void) size;
return nullptr;
}
void deallocate(T* ptr, std::size_t size) {
(void) ptr;
(void) size;
}
template<typename U>
struct rebind
{
typedef null_allocator<U> other;
};
};
int main(int argc, char** argv) {
std::vector<void*, null_allocator<void*>> vec;
void * args;
vec.push_back(args);
vec.erase(vec.begin());
}
gcc.godbolt.org shows that it compiles with Clang: http://goo.gl/VhKLCe
I pass a custom allocator to a std::vector, push a single object onto the vector, and try to call std::erase on that vector. The custom allocator is a null allocator that does nothing and is unimportant. The point is that this snippet compiles fine on Linux with both GCC and Clang, but fails to compile on OSX with Xcode/Apple Clang.
The output of clang --version is Apple LLVM version 7.0.0 (clang-700.1.76).
It appears that the compiler cannot complete std::iterator_traits:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:604:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iterator:1120:54: error: no type named 'iterator_category' in
'std::__1::iterator_traits<void **const>'
typedef typename iterator_traits<iterator_type>::iterator_category iterator_category;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
bug.cpp:42:13: note: in instantiation of template class 'std::__1::__wrap_iter<void **const>' requested here
vec.erase(vec.begin());
^
In file included from bug.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:604:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iterator:1121:54: error: no type named 'value_type' in
'std::__1::iterator_traits<void **const>'
typedef typename iterator_traits<iterator_type>::value_type value_type;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iterator:1122:54: error: no type named 'difference_type' in
'std::__1::iterator_traits<void **const>'
typedef typename iterator_traits<iterator_type>::difference_type difference_type;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iterator:1123:54: error: no type named 'pointer' in
'std::__1::iterator_traits<void **const>'
typedef typename iterator_traits<iterator_type>::pointer pointer;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iterator:1124:54: error: no type named 'reference' in
'std::__1::iterator_traits<void **const>'
typedef typename iterator_traits<iterator_type>::reference reference;
Does anyone know a good workaround for this issue?
I don't know why this works, but if you change using const_pointer = const pointer; in your custom allocator to using const_pointer = const T *; it seems to work for me (even on OS X using Apple's Clang).
Consider this template member method of some class:
template<typename T>
bool elementIsInSharedPtrVector(const T& p_elem, const std::vector< boost::shared_ptr< T > >& p_Vector) const
{
return (std::find_if(p_Vector.begin(), p_Vector.end(), **boost::lambda::_1 == p_elem) != p_Vector.end());
}
The compiler gives this error(besides hundreds of template errors):
usr/include/boost/pointee.hpp:30: error: no type named 'element_type' in 'struct SLnAdjW'
The type SLnAdjW is a POD C struct with a free defined == operator function.
What I'm doing wrong here?
I am having a problem in some code using type_traits from boost.
It is quite a complex part of the code, but I could isolate the part that gives the compilation error:
template<const size_t maxLen>
class MyString {
public:
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
};
template <class T>
class APIBase {
public:
typedef T obj_type;
typedef typename T::ObjExternal return_type;
};
template <class T>
int edit(const T& field, const typename T::return_type& value)
{
return 0;
}
int myFunction()
{
APIBase<MyString<10> > b;
char c[11];
return edit(b, c);
}
This gives the following error:
test.cpp: In function ‘int myFunction()’:
tes.cpp:109: error: no matching function for call to ‘edit(APIBase >&, char [11])’
tes.cpp:100: note: candidates are: int edit(const T&, const typename T::return_type&) [with T = APIBase >]
However, if I change the line with the code
char c[11];
by
MyString<10>::ObjExternal c;
it works. Similarly, if instead I change the line
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
by
typedef char ObjExternal[maxLen+1];
it also works. I am thinking that it is a problem with boost::conditional, as it seems it is not being evaluated right. Is there a problem in my code, or there is an alternative that can be used instead of boost::conditional to have this functionality?
I am thinking about using the 2nd option, but then I could not use maxLen as 0.
You need to use the member typedef type provided by conditional and not the conditional type itself.
Change:
typedef boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string> ObjExternal;
to:
typedef typename boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string>::type ObjExternal;
My C++ is a little rusty having worked in Java and C# for the last half dozen years. I've got a stupid little error that I just cannot figure out.
I've pared the code down as much as possible.
#include <list>
template<class T> class Subscriber
{
virtual void published( T t ) = 0;
};
template <class T> class PubSub
{
private:
std::list< Subscriber<T>* > subscribers;
public:
void publish( T t );
};
template<class T> void PubSub<T>::publish( T t )
{
for( std::list< Subscriber<T>* >::iterator i = subscribers.begin(); i != subscribers.end(); ++i )
i->published( t );
}
When I try and compile this (by including this header file in a code file), I get the following error:
../util/pubsub.h: In member function ‘void PubSub<T>::publish(T)’:
../util/pubsub.h:18: error: expected `;' before ‘i’
../util/pubsub.h:18: error: ‘i’ was not declared in this scope
What am I missing here?
for( typename std::list< Subscriber<T>* >::iterator i = ...
^^^^^^^^
for( typename std::list< Subscriber<T>* >::iterator i = subscribers.begin(); i != subscribers.end(); ++i )
You need the typename because iterator is a dependent name. The compiler has to check the template type T before it knows whether iterator is a type or a value. In those cases, it assumes it to be a value, unless you add typename.
This
std::list< Subscriber<T>* >::iterator
needs to be this
typename std::list< Subscriber<T>* >::iterator
The compiler assumes nested names in templates are static variables (not types) until told otherwise.