Template template parameter errors in MSVC, but not Clang. Why? - c++

I have written this code to help me sort indices that refer to a collection, according to some predicate:
#include <algorithm>
#include <functional>
#include <vector>
template<template<class> class Pred = std::less>
struct element_is_pred
{
template<class C>
struct type : private Pred<typename C::value_type>
{
typedef Pred<typename C::value_type> Base;
C const *c;
type(C const &c, Base const &pred = Base())
: Base(pred), c(&c) { }
bool operator()(
typename C::size_type const i,
typename C::size_type const j) const
{ return this->Base::operator()((*c)[i], (*c)[j]); }
};
};
template<template<class> class P, class C>
static element_is_pred<P>::template type<C const> element_is(
C const &c,
P<typename C::value_type> const &pred = P<typename C::value_type>())
{
return typename element_is_pred<P>::template type<C const>(c, pred);
}
and I'm using it like this:
int main()
{
std::vector<size_t> temp;
std::vector<size_t> indices;
indices.push_back(0);
std::stable_sort(
indices.begin(),
indices.end(),
element_is<std::less>(temp));
}
and when I compile it with Clang 3.2:
clang++ -fsyntax-only Test.cpp
it compiles fine.
But when I try to compile it with Visual C++ 2013, I get tons of errors, like:
test.cpp(23) : warning C4346: 'element_is_pred<Pred>::type<const C>' : dependent name is not a type
prefix with 'typename' to indicate a type
test.cpp(23) : error C2146: syntax error : missing ';' before identifier 'element_is'
test.cpp(23) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
Which compiler is correct?
What is the correct way to write the code to do this?

GCC gives the following error:
error: need 'typename' before 'element_is_pred<Pred>::type<const C>' because 'element_is_pred<Pred>' is a dependent scope
Following that advice, I can get the program to build on GCC by prepending typename:
static typename element_is_pred<P>::template type<C const> element_is(
^^^^^^^^
Clang allows the modified version as well.

Related

Apple Clang: can't compile call to std::erase for vector with custom allocator

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

A piece of code cannot be compiled by intel compiler but clang will compile it

The following code is a minimum working (or perhaps non-working) example.
What it does is basically encapsulates a bunch of std::map structures as private members in a base class. To avoid writing a lot of setters and getters, they are implemented as template functions.
// test.cpp
#include <map>
#include <iostream>
enum class E0
{
F0, F1, F2,
};
The declaration of the base class.
using std::map;
class P_base
{
private:
map<E0, int> m_imap;
// ...
// ... Other std::map members with different key types and value types.
public:
map<E0, int> & imap;
// ...
// ... Other std::map references.
P_base() : imap(m_imap) {}
template<typename map_type, typename key_type, typename val_type>
void set(map_type & m, const key_type & k, const val_type & v)
{
m[k] = v;
}
template<typename map_type, typename key_type>
auto access_to_map(const map_type & m, const key_type & k) -> decltype(m.at(k))
{
return m.at(k);
}
};
class P : private P_base
{
public:
decltype(P_base::imap) & imap;
P() : P_base(), imap(P_base::imap) {}
template<typename map_type, typename key_type, typename val_type>
void set(map_type & m, const key_type & k, const val_type & v)
{
P_base::set(m, k, v);
}
template<typename map_type, typename key_type>
auto access_to_map(const map_type & m, const key_type & k) -> decltype(P_base::access_to_map(m, k))
{
return P_base::access_to_map(m, k);
}
};
main
int main(int argc, const char * argv[])
{
using std::cout;
using std::endl;
P op;
op.set(op.imap, E0::F0, 100);
op.set(op.imap, E0::F1, 101);
op.set(op.imap, E0::F2, 102);
cout << op.access_to_map(op.imap, E0::F1) << endl;
}
$ clang++ -std=c++11 test.cpp && ./a.out
101
But if I compile it with intel compiler (icpc version 15.0.3 (gcc version 5.1.0 compatibility)), the compiler gives me this error message (which I don't undertand at all, especially when clang will compile the code):
$ icpc -std=c++ test.cpp && ./a.out
test.cpp(67): error: no instance of function template "P::access_to_map" matches the argument list
argument types are: (std::__1::map<E0, int, std::__1::less<E0>, std::__1::allocator<std::__1::pair<const E0, int>>>, E0)
object type is: P
cout << op.access_to_map(op.imap, E0::F1) << endl;
And it also confuses me by not complaining about the set function.
Does anyone have any idea what is going on here?
Note: My answer applies to g++ - hopefully it's the same as icc.
Here is a smaller test case:
struct Base
{
int func(int t) { return t; }
};
struct Der : Base
{
template<typename T>
auto f(T t) -> decltype(Base::func(t))
{
return t;
}
};
int main(){ Der d; d.f(5); }
The error is:
mcv.cc: In function 'int main()':
mcv.cc:16:25: error: no matching function for call to 'Der::f(int)'
int main(){ Der d; d.f(5); }
^
mcv.cc:16:25: note: candidate is:
mcv.cc:9:7: note: template<class T> decltype (t->Base::func()) Der::f(T)
auto f(T t) -> decltype(Base::func(t))
^
mcv.cc:9:7: note: template argument deduction/substitution failed:
mcv.cc: In substitution of 'template<class T> decltype (t->Base::func()) Der::f(T) [with T = int]':
mcv.cc:16:25: required from here
mcv.cc:9:38: error: cannot call member function 'int Base::func(int)' without object
auto f(T t) -> decltype(Base::func(t))
This can be fixed by changing decltype(Base::func(t)) to decltype(this->Base::func(t)). A corresponding fix fixes your code sample, for me.
Apparently, the compiler doesn't consider that Base::func(t) should be called with *this as hidden argument. I don't know if this is a g++ bug, or if clang is going beyond the call of duty.
Note that in C++14, since the function has a single return statement, the trailing return type can be omitted entirely:
template<typename T>
auto f(T t)
{
return t;
}

Code compiling on gcc, but not on msvc

I have a problem compiling a template using msvc-2010. It works perfectly using gcc 4.6.3.
I have boiled down the code to the essential (it doesn't make sense of course):
//Variant that works
template <typename T, T* Ptr>
void callFun()
{
}
//Traits class (type expands to the same type T* as above)
template <typename T>
class TraitsClass
{
public:
typedef T* type;
};
//Essentially the same as callFun2, only that the
//type of Ptr is expressed indirectly over a traits class
//The usage of this class is not possible, because of the error described below
template <typename T, typename TraitsClass<T>::type Ptr>
void callFun2()
{
}
//Provides a compile constant ptr for this example
void testFun()
{
}
int main()
{
//Works
callFun<void(), &testFun>();
//Fails
callFun2<void(), &testFun>();
//Works
callFun2<void(), 0>();
return 0;
}
The Error:
error C2975: 'Ptr' : invalid template argument for 'callFun2', expected compile-time constant expression
I find it interesting, that it only fails when the second type parameter is being used through a typedef in a Traits class.
g++ compiles this example correctly without warnings, even when using -Wall -Wextra -Werror -pedantic (Except for the unused parameters, of course)
Thank you very much.
Well, I think that the answer is that compilers are not written by gods. Programming standards in the compiler industry are extremely high, MS C++ is a good compiler, but it still contain bugs. I came across the following, that is somehow similar to what you are pointing at:
template <class item_struct>
struct THeapBasedArray
{
void Sort(int (__cdecl *compareFunction)(const item_struct *item1,
const item_struct *item2));
};
struct Item { int x; };
struct ItemPtrsArray : public THeapBasedArray<Item*>
{
static int __cdecl Compare1(const Item **pp1, const Item **pp2);
typedef Item *ItemPtr;
static int __cdecl Compare2(const ItemPtr *pp1, const ItemPtr *pp2);
};
int main()
{
ItemPtrsArray vect;
vect.Sort(ItemPtrsArray::Compare1);
vect.Sort(ItemPtrsArray::Compare2);
}
The first call to Sort fails with:
cpptest1.cxx(21) : error C2664: 'THeapBasedArray::Sort' : cannot convert parameter 1 from 'int (_cdecl *)(const Item **, const Item **)' to 'int (_cdecl *)(const item_struct *, const item_struct *)
while the second call compilers fine. For me this is a bug in a compiler. Sometimes this happens. I guess this is the answer.

expected ';' after expression

I'm trying to port a project from Windows to Mac. I'm having troubles compiling the class CFactory. I'll try to illustrate the problem.
Here's what I have on my Factory.h
namespace BaseSubsystems
{
template <class T>
class CFactory
{
protected:
typedef T (*FunctionPointer)();
typedef std::pair<std::string,FunctionPointer> TStringFunctionPointerPair;
typedef std::map<std::string,FunctionPointer> TFunctionPointerMap;
TFunctionPointerMap _table;
public:
CFactory () {}
virtual ~CFactory();
}; // class CFactory
template <class T>
inline CFactory<T>::~CFactory()
{
TFunctionPointerMap::const_iterator it = _table.begin();
TFunctionPointerMap::const_iterator it2;
while( it != _table.end() )
{
it2 = it;
it++;
_table.erase(it2);
}
} // ~CFactory
}
When I'm trying to compile, the compiler complains:
Factory.h:95:44: error: expected ';' after expression [1]
TFunctionPointerMap::const_iterator it = _table.begin();
^
;
Why is this happening? That am I missing?
NOTE: This project compiles properly on MSVC.
Thanks.
You are missing the required typename keyword when referring to a dependent type. Microsoft Visual Studio incorrectly accepts your code without the typename (this is a well known mistake in VS that will never be corrected).
typename TFunctionPointerMap::const_iterator it;

functor in header file

I have the following functor and I had included it in my main program
template<class T> struct Comp: public binary_function<T, T, int>
{
int operator()(const T& a, const T& b) const
{
return (a>b) ? 1: (a<b) ? -1 :0;
}
};
It wasn't giving any error when it was in the .cpp, but now when I moved it to my .h, it gives me the following error:
testclass.h: At global scope:
testclass.h:50:59: error: expected template-name before ‘<’ token
testclass.h:50:59: error: expected ‘{’ before ‘<’ token
testclass.h:50:59: error: expected unqualified-id before ‘<’ token
So, I rewrote it as:
template<class T> T Comp: public binary_function<T, T, int>
{
int operator()(const T& a, const T& b) const
{
return (a>b) ? 1: (a<b) ? -1 :0;
}
};
and now i get the following error:
testclass.h: At global scope:
testclass.h:50:30: error: expected initializer before ‘:’ token
any suggestion on how I can fix it? thanks!
The original error is probably with binary_function: missing the include or not considering that it is in namespace std.
#include <functional>
and
std::binary_function<T, T, int>
template<class T> T Comp: public binary_function<T, T, int> is not valid syntax, the first one is correct. The error is probably about binary_function — make sure you included the header and it should be std::binary_function.
Also, binary_function is largely useless, especially in C++11.
template<class T> T Comp : public binary_function<T, T, int>
//^^^ what is this?
What is that? That should be struct (or class).
Also, have you forgotten to include <functional> header file in which binary_function is defined?
Include <functional>. And use std::binary_function, instead of binary_function as:
#include <functional> //must include
template<class T> struct Comp: public std::binary_function<T, T, int>
{ //^^^^^ qualify it with std::
int operator()(const T& a, const T& b) const
{
return (a>b) ? 1: (a<b) ? -1 :0;
}
};