The following code compiles with gcc (g++) but complains with clang (c++).
I was expecting the line n1::generic(*it); to look for a my_traits<bool> (or my_traits<const bool>) specialization but, it seems to be looking for the name specific within the vector (const?) from within that same specialization.
Also, this is specific to bool. Other types like int work fine. (I tried adding a my_traits<vector<bool> > (and also const bool) specialization but it didn't help).
#include <vector>
namespace n1 {
template <class T> struct my_traits { };
template <> struct my_traits<bool> {
static void specific(bool b) { }
};
template <> struct my_traits<int> {
static void specific(int b) { }
};
template <typename T> void generic(const T& t)
{
my_traits<T>::specific(t);
}
template <typename T> struct my_traits<std::vector<T> > {
static void specific(const std::vector<T>& b)
{
if (! b.empty()) {
for (typename std::vector<T>::const_iterator it = b.begin();
it != b.end(); ++it) {
n1::generic(*it);
}
}
}
};
}
namespace n2 {
struct ArrayOfBoolean {
std::vector<bool> values;
};
struct ArrayOfInt {
std::vector<int> values;
};
}
namespace n1 {
template<> struct my_traits<n2::ArrayOfBoolean> {
static void specific(const n2::ArrayOfBoolean& v) {
n1::generic(v.values);
}
};
template<> struct my_traits<n2::ArrayOfInt> {
static void specific(const n2::ArrayOfInt& v) {
n1::generic(v.values);
}
};
}
c++ codec.cc -o codec
In file included from codec.cc:1:./codec.h:17:23: error: no member named 'specific' in 'n1::my_traits<std::__1::__bit_const_reference<std::__1::vector<bool,std::__1::allocator<bool> > > >'
my_traits<T>::specific(t);
~~~~~~~~~~~~~~^
./codec.h:26:25: note: in instantiation of function template specialization 'n1::generic<std::__1::__bit_const_reference<std::__1::vector<bool, std::__1::allocator<bool> > > >' requested here
n1::generic(*it);
^
./codec.h:17:23: note: in instantiation of member function 'n1::my_traits<std::__1::vector<bool, std::__1::allocator<bool> > >::specific' requested here
my_traits<T>::specific(t);
^
./codec.h:47:17: note: in instantiation of function template specialization 'n1::generic<std::__1::vector<bool, std::__1::allocator<bool> > >' requested here
n1::generic(v.values);
^
1 error generated.
Odd as it seems, a vector<bool> doesn't hold any bools. Only bits which you cannot get a real reference to.
So in n1::generic(*it); there is no const bool&, only a __bit_const_reference proxy class.
Related
I want to change the variable type of member variable when type parameter is the specific type
(ex. int to unsigned char).
The error messages are added in comment.
template <typename T>
class temClass {
private:
template<typename T> // typename : declaration of template parameter 'T' shadows template parameter
struct STR_T {
typedef T T_IF;
};
template<> // <> : error: explicit specialization in non-namespace scope 'class temClass<T>'
struct STR_T<int> { // STR_T<int> : template parameters not deducible in partial specialization
typedef unsigned char T_IF;
};
typedef typename STR_T<T>::T_IF T_DEF;
T_DEF abc;
public:
temClass() { }
~temClass() { }
void showMemSize() {
printf("mem size = %d\n", sizeof(abc));
}
};
and the main function
int main() {
temClass<int> temClassInt;
temClass<unsigned int> temClassUInt;
temClassInt.showMemSize();
temClassUInt.showMemSize();
return 0;
}
The desired result is 1 and 4.
I use eclipse CDT with MinGW GCC. the GCC version is 6.3.0.
i'm so dumb
thanks StoryTeller - Unslander Monica 's comment
just move STR_T to namespace
namespace NS_STR {
template<typename T2>
struct STR_T {
typedef T2 T_IF;
};
template<>
struct STR_T<int> {
typedef unsigned char T_IF;
};
}
template <typename T>
class temClass {
private:
typedef typename NS_STR::STR_T<T>::T_IF T_DEF;
T_DEF abc;
public:
temClass() { }
~temClass() { }
void showMemSize() {
printf("mem size = %d\n", sizeof(abc));
}
};
The result would be 1 and 4
I was experimenting with C++ template metaprogramming trying to create a storage class with the following semantics: it takes arbitrary number of types and stores a container of user-defined type for each of them with common access interface. I was able to implement it with following code using multiple inheritance from decltype-expanded list (A and B are just dummy structures to be put into Storage):
#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
struct A
{
int v = -1;
};
struct B
{
std::string v;
};
typedef int Key;
template<typename T>
auto componentContainer();
template<>
auto componentContainer<A>()
{
return std::unordered_map<Key, A>();
}
template<>
auto componentContainer<B>()
{
return std::map<Key, B>();
}
template<typename... Component>
struct Storage : public decltype(componentContainer<Component>())...
{
template <typename T>
using Container = decltype(componentContainer<T>());
template<typename T>
T& get(int index)
{
return Container<T>::operator [](index);
}
template<typename T>
const T& get(int index) const
{
return Container<T>::operator [](index);
}
template<typename T>
void put(int index, const T& v)
{
Container<T>::operator [](index) = v;
}
template<typename T, typename F>
void apply(F f)
{
for (auto p = Container<T>::begin();
p != Container<T>::end();
p++)
{
f(p);
}
}
};
int main(int argc, char** argv)
{
Storage<A,B> s;
s.put<A>(0, {12});
s.put<A>(3, {42});
s.put<B>(0, {"melta"});
s.put<B>(42, {"multimelta"});
auto printer = [](auto p) { std::cout <<p->first <<": " << p->second.v <<std::endl;};
s.apply<A>(printer);
s.apply<B>(printer);
return 0;
}
This code compiles just fine in gcc 5.1.0 and produces an expected result, but fails to compile in Visual Studio 2015 with following error message:
main.cpp(37): error C2143: syntax error: missing ',' before '...'
main.cpp(70): note: see reference to class template instantiation 'Storage<Component...>' being compiled
main.cpp(37): error C3520: 'Component': parameter pack must be expanded in this context
main.cpp(74): note: see reference to class template instantiation 'Storage<A,B>' being compiled
main.cpp(37): error C3770: 'unknown-type': is not a valid base class
The thing is, I'm not sure if it's legal (i.e., standard-compliant) to inherit from expanded decltype list like that. So, my questions are:
Is struct Storage: public decltype(componentContainer<Component>())... a legal thing in standard C++ or is it a gcc feature?
If it is, can in be done in Visual Studio?
This works for me in MSVC.
template<typename T>
struct StorageBase
{
using Type = decltype(componentContainer<T>());
};
template<typename... Component>
struct Storage : public StorageBase<Component>::Type...
{ }
The syntax error leads me to believe that the compiler is trying to evaluate the decltype expression before expanding the parameter pack - thus why it is also emitting 'Component': parameter pack must be expanded in this context.
Simplifying the expression by using StorageBase to do the dirty work with decltype looks to do the job.
Instead of inheritance, you might use composition (thanks to std::tuple):
template <typename T>
using Container = decltype(componentContainer<T>());
template <typename... Components>
class Storage
{
public:
template<typename T>
T& get(int index) { return std::get<Container<T>>(t)[index]; }
template<typename T>
const T& get(int index) const { return std::get<Container<T>>(t).at(index); }
template<typename T>
void put(int index, const T& v) { std::get<Container<T>>(t)[index] = v; }
template<typename T, typename F>
void apply(F f)
{
for (const auto& p : std::get<Container<T>>(t))
{
f(p);
}
}
private:
std::tuple<Container<Components>...> t;
};
I have a class A, which has an unordered container with instances of a class B, B depends on A, as it has a pointer to its A-instance as a field. I can forward declare B before the implementation of A, but that is insufficient, because the unordered container in Arequires the definition of std::hash<B>, which hover cannot be defined before A, because it depends on it.
template <typename X>
class B;
namespace std
{
template <typename V>
struct hash<B<V>> // <- requires full definition
{
size_t operator()(const B<V>& b) const
{
return (b.mem /*do hashing stuff with it*/ );
}
};
}
template <typename T>
class A
{
typedef A<T> THIS;
void func()
{
}
std::unordered_set<B<THIS>> set;// <- requires std::hash<B>
};
template <typename A>
class B
{
B(A* a)
{
A_ptr = a;
}
void otherfunc()
{
A_ptr->func();
}
public:
int mem;
A* A_ptr;
};
Is there a way to resolve this?
Brequires A to be complete. Adoes not require B to be complete, but it requires std::hash<B>, which itself requires B to be complete.
Edit:
I tried to incorporate Richard Hodges proposal into my actual program, but I can't get it to work. This is the order in wich the code is compiled in my files:
namespace E
{
template<typename G>
class R;
template <typename V, typename P>
class G;
}
namespace std
{
template <typename G> std::size_t hash_code(const E::R<G>&);
template <typename G>
struct hash<E::R<G>>
{
size_t operator()(const E::R<G>& r) const
{
return hash_code(r);
}
};
}
namespace E
{
template <typename V, typename P>
class G
{
// code
}
template <typename G>
class R
{
// code
}
}
namespace std
{
template<typename G>
size_t hash_code(const E::R<G>& r)
{
size_t hash = 0x9e3779b9;
typename E::R<G>::Rside v = r[0];
for(auto t = v.begin(); t != v.end(); ++t)
{
hash += (((*t + (hash << 6)) ^ (hash >> 16)) - hash);
}
v = r[1];
for(auto t = v.begin(); t != v.end(); ++t)
{
hash += (((*t + (hash << 6)) ^ (hash << 16)) - hash);
}
return hash;
}
}
But I am getting
implicit instantiation of undefined template
'std::__1::hash<std::__1::vector<std::__1::vector<int, std::__1::allocator<int>
>, std::__1::allocator<std::__1::vector<int, std::__1::allocator<int> > > > >'
: public integral_constant<bool, __is_empty(_Tp)> {};
and
/usr/include/c++/5.2.0/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > >) (const std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
There are too many errors in your code to provide a working example, but this will get you started.
There are many ways to do this, but in my view the cleanest is via a free function called hash_code which is found by ADL.
something like this:
// forward declarations
template <typename X> class B;
template <typename X> std::size_t hash_code(const B<X>&);
// specialisation of std::hash, uses only references so forward declarations are fine.
template <typename V>
struct std::hash<B<V>> // <- no longer requires full definition
{
size_t operator()(const B<V>& b) const {
return hash_code(b);
}
};
...
... later on ...
...
// provide the definition of hash_code once B has been defined.
template<typename A>
std::size_t hash_code(const B<A>& b) {
return b.mem;
}
I have this simple code below, a template with 2 type parameters. If I declare my class with the same type (like BidirectionalMap<int,int>), I receive an error:
int BidirectionalMap<T,S>::operator [](T) const' : member function already defined or declared
Here's my template code:
template <class T, class S>
class BidirectionalMap{
int count(T t){
return 1;
}
int count(S s){
return 1;
}
};
The error you got is normal, because after substitution you have
template <>
class BidirectionalMap<int, int>
{
int count(int t){ return 1; }
int count(int s){ return 1; } // Duplicated method
};
To solve that, you may provide partial specialization:
template <class T>
class BidirectionalMap<T, T>
{
int count(T t) { return 1; }
};
In C++20, you might use requires to "discard" methods:
template <class T, class S>
class BidirectionalMap
{
int count(T t) requires(!std::is_same<T, S>::value) { /*..*/ }
int count(S s) requires(!std::is_same<T, S>::value) { /*..*/ }
int count(T t) requires( std::is_same<T, S>::value) { /*..*/ }
};
template<typename T1, typename T2>
class Bimap {
public:
class Data {
private:
template<typename T> Data& set(T);
template<> Data& set<T1>(typename T1 v) { /*...*/ }
};
};
That gives me the error:
error: explicit specialization in non-namespace scope 'class Bimap<T1, T2>::Data'
I understand what the error is saying. But why I can't I do this? And how can I fix it?
One way forget templates, overload:
Data& set(T1 v) { /*...*/ }
but here is a trick which I use sometimes
you can specialize class template within class:
class {
template<typename T>
struct function_ {
static void apply(T);
};
template<>
struct function_<int> {
...
};
template<typename T>
void function(T t) { return function_<T>::apply(t); }
#Albert
I had a similar problem when I wanted to add a "trim-excess-capacity" to a custom made container. The std::vector swap trick and changing the declaration of the existing container were not valid options. So I've come up with this:
template <class T, bool isPtr> struct DeleteImp
{
static void Trim(T* to, unsigned int count);
};
template <class T> struct DeleteImp<T, false>
{
static void Trim(T* to, unsigned int count) {}
};
template <class T> struct DeleteImp<T, true>
{
static void Trim(T* to, unsigned int count)
{
for(unsigned int i=0; i<count; i++)
delete to[i];
}
};
used by my container like this:
DeleteImp<T, TypeTraits<T>::isPointer>::Trim(buf + length, truelength-length);
You may also want to check out this resource.