I am trying to partially specialize the std::hash struct for my own class TestHandle, and this class has its implementation split up using the opaque pointer idiom. So I am trying to provide the impl class with its own std::hash specialization. But I am running into templating problems.
Could someone help me understand why this is happening? I have attached all the necessary code below.
TestHandle.h
#pragma once
#include <memory>
class TestHandle {
public:
TestHandle();
void print();
class Impl;
std::unique_ptr<Impl> implementation;
};
TestHandle.cpp
#include "TestHandle.h"
#include "Impl.h"
#include <iostream>
using std::cout;
using std::endl;
TestHandle::TestHandle() : implementation{new TestHandle::Impl} { }
void TestHandle::print() {
this->implementation->print();
cout << "Hash of this->implementation is "
<< std::hash<TestHandle::Impl>()(*this->implementation) << endl;
}
Impl.h
#pragma once
#include "TestHandle.h"
#include <functional>
class TestHandle::Impl {
public:
void print();
int inner_integer;
};
namespace std {
template <> struct std::hash<TestHandle::Impl>;
}
Impl.cpp
#include "TestHandle.h"
#include "Impl.h"
#include <iostream>
using std::cout;
using std::endl;
#include <functional>
namespace std {
template <> struct hash <TestHandle::Impl> {
size_t operator() (const TestHandle::Impl& implementation) {
return std::hash<int>()(implementation.inner_integer);
}
};
}
void TestHandle::Impl::print() {
cout << "Printing from impl" << endl;
}
I am compiling with the following command
g++ -std=c++14 -c Impl.cpp TestHandle.cpp
and am getting the following error
TestHandle.cpp:11:12: error: invalid use of incomplete type 'std::hash<TestHandle::Impl>'
<< std::hash<TestHandle::Impl>()(*this->implementation) << endl;
template <> struct std::hash<TestHandle::Impl>;
Just forward declares the specialisation. It doesn't have to implement all the method (or any) of the original template. The compiler has no idea about the operator().
You will need to define the struct (in place of just the declaration);
template <> struct hash <TestHandle::Impl> {
size_t operator() (const TestHandle::Impl& implementation) const noexcept;
};
Side note: you will also need to provide the primary template (via inclusion) of <functional> (missing in the original listed code).
Related
I am attempting to create a templated vector class, but upon compilation I am receiving an error of
def.hpp:3:1: error: 'TempVector' does not name a type
I keep referring to reference material and my syntax and handling of the header file declaration and definition (.h and .hpp) seem right to me, but I can not figure out what I am overlooking.
Below is the three files I am working with, thank you.
driver.cpp:
#include <iostream>
#include <string>
#include "dec.h"
using namespace std;
int main() {
TempVector <int> v1;
cout<<"ran successfully"<<endl;
}
dec.h:
#ifndef DEC_H
#define DEC_H
#include <iostream>
#include <utility>
// Declaration of class Vector
template <typename T>
class TempVector {
public:
TempVector ();
private:
T* array;
static const unsigned int spare = 10;
};
#include "def.hpp"
#endif
def.hpp:
template <typename T>
TempVector<T>::TempVector () {
std::cout<<"ran successfully";
}
I would like to put the implementation of the specializations of a templated function to a separate source file, but if I try to call it (in MyAction), I get this error:
Explicit specialization has already been instantiated
I have no idea, why I get this error.
Example code:
main.cpp
#include <iostream>
#include <string>
#include "MyClass.h"
int main()
{
std::cout << "Hello, " << XX::MyClass().MyMethod<1>() << std::endl;
std::cin.get();
}
MyClass.h
#pragma once
#include <string>
namespace XX {
struct MyClass {
std::string MyAction() {
return MyMethod<0>() + MyMethod<1>();
}
template<int>
std::string MyMethod();
};
template<>
std::string MyClass::MyMethod<0>();
template<>
std::string MyClass::MyMethod<1>();
}
MyClass.cpp
#include "MyClass.h"
namespace XX {
template<>
std::string MyClass::MyMethod<0>() {
return "FOO";
}
template<>
std::string MyClass::MyMethod<1>() {
return "BAR";
}
}
Is there a template instantiation rule, which I am not aware of?
Ok looks like problem is an order.
When you are defining MyAction compiler tries instantiate template, but he doesn't know about specialization.
When you declare MyAction and define it in cpp after template specialization it will work.
// header part
#include <string>
namespace XX {
struct MyClass {
template<int>
std::string MyMethod();
std::string MyAction();
};
}
// cpp part
namespace XX {
template<>
std::string MyClass::MyMethod<0>() {
return "a";
}
template<>
std::string MyClass::MyMethod<1>() {
return "b";
}
std::string MyClass::MyAction() {
return MyMethod<0>() + MyMethod<1>();
}
}
See here: https://godbolt.org/z/aGSB21
Note if you move MyClass::MyAction() above MyClass::MyMethod<1>() error will come back.
Here is version where specialization is can be declared in header file: https://godbolt.org/z/kHjlne
I've distilled my problem to a small example, pardon the macros. It seems a similar problem from this post is no longer an issue in VS and compiles fine. I believe I have a more specialized version of this problem that hasn't been fixed but want to make sure I'm not missing something. The following code compiles in GCC and runs expectedly, but gives error C2893 (Failed to specialize function template) in VS:
Macros.h:
#define If(x) \
template<class T,class...Args, typename std::enable_if<std::is_same<T, x>::value>::type* = nullptr>
#define Do void Func(Args... args)
Definition.cpp:
#include <string>
#include <iostream>
#include "Macros.h"
using namespace std;
int answer = 42;
double pie = 3.14;
string s = "Hello World";
// Function Definitions
If(int) Do { cout << answer << endl; }
If(double) Do { cout << pie << endl; }
If(string) Do { cout << s << endl; }
// Explicit Instantiations
template void Func<int>(int, double, string);
template void Func<double>();
template void Func<string>();
Usage.cpp:
#include <string>
#include <type_traits>
#include "Macros.h"
// Template Function Declaration
If(T) Do;
int main() {
using namespace std;
Func<int>(5, 2.0, string("hello"));
Func<double>();
Func<string>();
}
As with the example in the other post it works correctly if the instantiations come from actual use in a function. Doing so is simple with this example but not so simple with my code.
/* MyClass.h */
class MyClass
{
public:
template <typename T>
void Foo(const T &val);
};
/* MyClass.cpp */
#include <string>
#include <iostream>
#define EXPLICIT_INSTANTIATION_FOO(MyType) \
template void MyClass::Foo(const MyType &val)
EXPLICIT_INSTANTIATION_FOO(int);
EXPLICIT_INSTANTIATION_FOO(float);
EXPLICIT_INSTANTIATION_FOO(std::string);
template <typename T>
void MyClass::Foo(const T &val)
{
std::cout << "My Value: " << val << std::endl;
}
For some other reason, I couldn't define the Foo in the header file, then I try to explicitly instantiate all the types I need in the source file. And it works pretty well on g++-4.8.
Then once it I submit the codes, I find the building failed on Windows 10, x86_64, cl19. It was an unresolved symbol error there and was look like my instantiations weren't be exposed from the object.
But interestingly, when I try to reproduce this error on my own Windows by making that simple snippet above. I found it works well on my vs2017 with cl19...
There would be thousands of configuration differs between the company and the personal machine, but I just don't have any idea that what would be the reason why the instantiations don't work there... Any ideas appreciated! :)
SUPPLEMENTS:
FYI, the complete codes are here:
/* MyClass.h */
#pragma once
class MyClass
{
public:
MyClass();
~MyClass();
template <typename T>
void Foo(const T &val);
};
/* MyClass.cpp */
#include "MyClass.h"
#include <iostream>
#include <string>
#define EXPLICIT_INSTANTIATION_FOO(MyType) \
template void MyClass::Foo(const MyType &val)
EXPLICIT_INSTANTIATION_FOO(int);
EXPLICIT_INSTANTIATION_FOO(float);
EXPLICIT_INSTANTIATION_FOO(std::string);
MyClass::MyClass() {}
MyClass::~MyClass() {}
template <typename T>
void MyClass::Foo(const T &val)
{
std::cout << "My Value: " << val << std::endl;
}
/* main.cpp */
#include "MyClass.h"
#include <iostream>
#include <string>
int main()
{
std::string v = "hello";
MyClass cls;
cls.Foo(v);
std::cin.get();
return 0;
}
Which passed the compilation on msvc.
Move the function definition to before the EXPLICIT_INSTANTIATION_FOO invocations. This is something that never should have worked but compilersllowed, they are slowly coming around on it. If you want to make msvc reject it use the /permissive- flag.
Thanks, everybody, this silly question is solved.
The reason is the template instantiations will not inherit the dllexport attribute from the template definition on Windows. And obviously, it works on my silly test codes because it wasn't compiled as the DLL :)
I am newbie in programming with c++ template. I have 3 code files
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input){}
};
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
template_test.cpp
#include "template_test.h"
//empty
The code takes effective in VS2013. However when I change my code to 2 sitiuations, there are both something wrong with my code.
1.The first is with the linker error code.
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input);
};
template<class T>
void mytest<T>::method(T input)
{
cout << " " << endl;
}//explicit specialization here
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
template_test.cpp
#include "template_test.h"
//empty
2.The second output nothing instead of the correct answer 'ok'.
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input){}
};
template_test.cpp
#include "template_test.h"
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}//move from .h to .cpp file here
The strange behaviour of c++ template makes me confused. So, what's the problem?
The first problem is caused by the fact that your explicit specialization
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
is defined in the header file and outside the class definition and without the keyword inline.
An explicit specialization causes an actual function (rather than a mere template) to be defined. That definition will occur in every translation unit, so if you compile template_test.cpp and main.cpp separately, the definition of the function will be included in both object files, causing a multiple-definition error at linking time (because it's a violation of the ODR, the one-definition-rule).
You best avoid this either by including the function definition inside the class template definition (by specializing the entire class template for int), or using the keyword inline:
template<>
inline void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
The second problem is caused by the fact that a template specialization must always be declared before it is used:
(14.7.3/6) If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. [...]
Since your main.cpp includes the header file, but not the .cpp file, the declaration of the template specialization is not known when it is used in main.cpp. You solve this best by including the specialization in the header file, either inside the class template definition (by specializing the entire class template for int), or using the keyword inline outside the class template definition.
You second question first. Remember a rule when using template. Do not separate the template implementation to a cpp file. Make them all in one .h file.
You first code compiles fine in my VC2012, I am not sure what's wrong with VC2013.