Why forward declaration as follows :
template<typename T> struct std::hash;
fails to compile with gcc and clang, but compiles with Visual Studio 2015?
gcc 6.1.0 (using coliru):
main.cpp:11:34: error: invalid use of template-name 'std::hash' without an argument list
template<typename T> struct std::hash;
^~~~
clang 3.8.0 (using coliru):
main.cpp:11:29: error: forward declaration of struct cannot have a nested name specifier
template<typename T> struct std::hash;
^~~~~
it works under VS (http://webcompiler.cloudapp.net/). Which compiler is right?
btw. the same declaration is used in C++ Primer 5th edition. Well - nearly the same it uses class instead of struct: template <class T> class std::hash; which is wrong.
full code:
#include <unordered_map>
/*
// compiles with gcc,clang,VS
namespace std {
template<typename T>
struct hash;
}*/
// Compiles only with VS
template<typename T> struct std::hash;
struct MyData {
MyData() {}
MyData(int d1, int d2) : data1(d1), data2(d2) {}
bool operator==(const MyData& rop) const {
return rop.data1 == data1 && rop.data2 == data2;
}
friend struct std::hash<MyData>;
private:
int data1;
int data2;
};
namespace std {
template<>
struct hash<MyData> {
typedef MyData argument_type;
typedef size_t result_type;
size_t operator()(const argument_type& data) const noexcept;
};
size_t hash<MyData>::operator()(const argument_type& data) const noexcept {
return hash<unsigned>()(data.data1) ^ hash<unsigned>()(data.data2);
}
}
int main() {
std::unordered_map<MyData, std::string> mm;
mm[MyData(1,1)] = "test1";
mm[MyData(2,2)] = "test1";
}
The reason, seems to be largely because a forward declaration has to function much like a regular declaration. i.e. encased in a namespace, not prefixed by one. I guess this would allow the same parser to be used for declarations and forward declarations which makes sense.
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 try to define a static variable outside the class scope like:
template<typename T>
struct Foo {
void set(int i) {
}
static constexpr decltype(&Foo<T>::set) i = &Foo<T>::set;
};
template<typename T>
constexpr decltype(&Foo<T>::set) Foo<T>::i;
Live example.
But I get following error (for all gcc >= 4.7):
conflicting declaration 'constexpr decltype (& Foo<T>::set(int)) Foo<T>::i'
note: previous declaration as 'constexpr decltype (& Foo<T>::set(int)) Foo<T>::i'
All clang version (clang >= 3.2) do not have any problem with my code.
The problem seems to be the function reference. It works without using a template class.
My questions:
Is it a bug?
How to do it in gcc?
I don't know if it's a bug or not, but you can do it like this:
template<typename T>
struct Foo {
void set(int i) {
}
typedef decltype(&Foo<T>::set) function_type;
static constexpr function_type i = &Foo<T>::set;
};
template<typename T>
constexpr typename Foo<T>::function_type Foo<T>::i;
int main()
{
Foo<int> f;
}
I am facing error C2783.
I reproduce error with similar structure test case.
Here is test case:
#include <iostream>
namespace ns1 {
template <class T> class class_1 {};
}
namespace ns2 {
using namespace ns1;
template <typename T> inline ns1::class_1<T> myfunc();
template<typename T>
inline ns1::class_1<T> myfunc() {
int a,b;
std::cin>>a;
std::cin>>b;
if(a<b) return true;
else return false;
}
}
namespace ns3 {
struct myStruct {
ns1::class_1<double> var1;
ns1::class_1<double> var2;
myStruct ( const ns1::class_1<double>& cl0= ns2::myfunc<double>(),
const ns1::class_1<double>& cl1= ns2::myfunc<double>()): var1(cl0), var2(cl1) {};
};
}
Error is :
error C2783: 'ns1::class_1 ns2::myfunc(void)' : could not deduce template argument for 'T'
Also i wonder why its giving error for line 27 (cl0) but not line 28 (for cl1)?
If I try to use this on some function its works fine only giving error when using in structure arguments.
This is a compiler bug. If you replace the contents of myfunc with valid code (as suggested), it still doesn't work. For a description, status (and acknowledgement) of the bug, see Microsoft Connect. You might try to use a helper type to get argument deduction (which works):
namespace ns1 {
template <class T> class class_1 {
public: class_1 (int a, int b){}
};
}
namespace ns2 {
template<class T> struct deduction_helper{};
using namespace ns1;
template <typename T> inline ns1::class_1<T> myfunc(deduction_helper<T>);
template<typename T>
inline ns1::class_1<T> myfunc(deduction_helper<T>) {
int a,b;
std::cin>>a;
std::cin>>b;
ns1::class_1<T> c(a,b); return c;
}
}
namespace ns3 {
struct myStruct {
ns1::class_1<double> var1;
ns1::class_1<double> var2;
myStruct ( const ns1::class_1<double>& cl0= ns2::myfunc(ns2::deduction_helper<double>()),
const ns1::class_1<double>& cl1= ns2::myfunc(ns2::deduction_helper<double>())
): var1(cl0), var2(cl1) {};
};
}
int main()
{
ns3::myStruct x;
}
N.B. as the helper type resides in ns2, you could use ADL instead of qualifying the name myfunc.
I'm trying to port a C++ project to iOS. It compiles just fine in QtCreator on Linux and Windows and also on MSVC.
Now on Xcode/GCC, with a certain templated class I get the following error: "error: too few template-parameter-lists".
The code that causes this error looks something like this:
template <typename TA, typename TB, int Type>
class MyClassImpl
{
public:
MyClassImpl();
virtual int GetType() const
{
return type;
}
};
typedef MyClassImpl<float, int, 12> MyFloatIntClass;
MyFloatIntClass::MyFloatIntClass()
{
...
}
int MyFloatIntClass::GetType() const
{
return 22;
}
I'm guessing that something about the typedef syntax is illegal and GCC is more strict on the standard.
Can anybody tell me what exactly the problem is and how I can fix it?
As you're defining full specializations of methods of the respective class, you'll still need to prefix the definitions with template <>, which is the "template-parameter-lists" you're missing. Furthermore, the constructor must be named as the class is, so MyFloatIntClass::MyFloatIntClass() is illegal (as MyFloatIntClass is just an alias, not a class name). The following compiles fine for me (g++ 4.5.3):
template <typename TA, typename TB, int Type>
class MyClassImpl
{
public:
MyClassImpl();
virtual int GetType() const
{
return Type;
}
};
typedef MyClassImpl<float, int, 12> MyFloatIntClass;
template <>
MyFloatIntClass::MyClassImpl()
{
}
template <>
int MyFloatIntClass::GetType() const
{
return 22;
}
This is just a guess, but do you need to add template<>?
Since it's a template specialization, I believe it still needs to be a template.
ex.
template<>
MyFloatIntClass::MyClassImpl() {}
template<>
int MyFloatIntClass::GetType() const {
return 22;
}
EDIT: From modelnine's answer- turns out it needs the untypedef'd name for the ctor.
EDIT2: The following code works fine for me:
template <typename TA, typename TB, int Type>
class MyClassImpl
{
public:
MyClassImpl();
MyClassImpl(const MyClassImpl<TA, TB, Type>&);
virtual int GetType() const
{
return Type;
}
const MyClassImpl<TA, TB, Type>& operator=(const MyClassImpl<TA, TB, Type>&);
};
typedef MyClassImpl<float, int, 12> MyFloatIntClass;
template<>
MyFloatIntClass::MyClassImpl()
{
//
}
template<>
MyFloatIntClass::MyClassImpl( const MyFloatIntClass& rhs )
{
//
}
template<>
const MyFloatIntClass& MyFloatIntClass::operator=( const MyFloatIntClass& rhs )
{
return *this;
}
template<>
int MyFloatIntClass::GetType() const
{
return 22;
}