I'm going crazy over here. I have search google to find 1 single decent example where people use a unordered_map together with enum class and a hash function without any luck. Those i manage to find always end up saying "use map instead".
I'm trying to do the following:
Enum class facing is the direction my sprite is looking at.
Enum class Action is the action my sprite is doing.
Animation is a class that holds different animations which i will call later.
The container should look like this:
map
There can be more than 1 FACING in the map as key and there can be more than one ACTION in the pair.
Example:
map<LEFT, pair<ATTACK, attackAnimation>
map<LEFT, pair<IDLE, idleAnimation>
map<LEFTUP, pair<IDLE, idleAnimation>
This is an simplified everything
#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>
template <typename T>
struct Hash
{
typedef typename std::underlying_type<T>::type underlyingType;
typedef typename std::hash<underlyingType>::result_type resultType;
resultType operator()(const T& arg) const
{
std::hash<underlyingType> hasher;
return hasher(static_cast<underlyingType>(arg));
}
};
class Animation
{
private:
std::string str;
public:
Animation(std::string _string)
{
this->str = _string;
}
std::string& GetString()
{
return this->str;
}
};
class Bullshit
{
public:
enum class Action
{
Attack,
Move
};
enum class Facing
{
Right,
Up,
Left
};
Bullshit()
{
}
std::unordered_multimap<Bullshit::Facing, std::pair<Bullshit::Action, std::unique_ptr<Animation>>,Hash<Bullshit::Facing>>& GetlistAnimation()
{
return this->listAnimation;
}
private:
std::unordered_multimap<Bullshit::Facing, std::pair<Bullshit::Action, std::unique_ptr<Animation>>,Hash<Bullshit::Facing>> listAnimation;
};
int main()
{
Bullshit bull;
auto myList = bull.GetlistAnimation();
std::unique_ptr<Animation> anim(new Animation("test"));
myList.insert(std::make_pair(Bullshit::Facing::Up, std::make_pair(Bullshit::Action::Attack, std::move(anim))));
std::cin.get();
return 0;
}
Error code:
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1> with
1> [
1> _Ty=Animation
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1> with
1> [
1> _Ty=Animation
1> ]
1> This diagnostic occurred in the compiler generated function 'std::pair<_Ty1,_Ty2>::pair(const std::pair<_Ty1,_Ty2> &)'
1> with
1> [
1> _Ty1=Bullshit::Action,
1> _Ty2=std::unique_ptr<Animation>
1> ]
Here
auto myList = bull.GetlistAnimation();
the type deduced for myList is std::unordered_map<.....>, that is, it's not a reference. And the copy can't be created because the map contains unique_ptrs. What you meant is
auto& myList = bull.GetlistAnimation();
or in C++14,
decltype(auto) myList = bull.GetlistAnimation();
The problem has nothing to do with unordered_map or hash functions. It's the std::unique_ptr, which is uncopyable, and your GetlistAnimation attempts to copy it (indirectly).
How to correctly fix this depends on what you want to achieve.
A quick fix would be to use std::shared_ptr instead:
std::unordered_map<Bullshit::Facing, std::pair<Bullshit::Action, std::shared_ptr<Animation>>,Hash<Bullshit::Facing>>& GetlistAnimation()
{
return this->listAnimation;
}
private:
std::unordered_map<Bullshit::Facing, std::pair<Bullshit::Action, std::shared_ptr<Animation>>,Hash<Bullshit::Facing>> listAnimation;
[...]
std::shared_ptr<Animation> anim(new Animation("test"));
myList.insert(std::make_pair(Bullshit::Facing::Up, std::make_pair(Bullshit::Action::Attack, anim)));
(By the way, you should use std::make_shared and std::make_unique.)
A fix which may be quick and correct (again, depending on what you want to achieve) is to get rid of the pointer logic altogether and just use Animation directly:
std::unordered_map<Bullshit::Facing, std::pair<Bullshit::Action, Animation>,Hash<Bullshit::Facing>>& GetlistAnimation()
{
return this->listAnimation;
}
private:
std::unordered_map<Bullshit::Facing, std::pair<Bullshit::Action, Animation>,Hash<Bullshit::Facing>> listAnimation;
[...]
Animation anim("test");
myList.insert(std::make_pair(Bullshit::Facing::Up, std::make_pair(Bullshit::Action::Attack, anim)));
Related
I want to build a hash table using a linked list
But there is a problem during compilation
I have tried using an array instead of a vector, but still the same result
Maybe it has something to do with the template?
I don't understand the sample code related to template
Here is some code that reproduces the problem, along with the error message
#include <iostream>
#include <vector>
using namespace std;
struct node
{
string key;
string value;
node* next;
};
class HashTable
{
private:
vector<node*> bucket;
long long int capacity;
public:
HashTable(long long int max_size = 100)
{
capacity = max_size;
bucket.reserve(capacity);
bucket.resize(capacity);
fill(bucket.begin(), bucket.end(), NULL);
}
};
Build started...
1>------ Build started: Project: C++ Test, Configuration: Debug x64 ------
1>C++ Test.cpp
1>E:\Other\Visual Studio\VC\Tools\MSVC\14.33.31629\include\xutility(4519,24): error C2440: '=': cannot convert from 'const _Ty' to 'node *'
1> with
1> [
1> _Ty=int
1> ]
1>E:\Other\Visual Studio\VC\Tools\MSVC\14.33.31629\include\xutility(4519,24): message : Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or parenthesized function-style cast
1>C:\Users\DaLaw2\source\repos\C++ Test\C++ Test.cpp(30): message : see reference to function template instantiation 'void std::fill<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>,int>(const _FwdIt,const _FwdIt,const int &)' being compiled
1> with
1> [
1> _Ty=node *,
1> _FwdIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<node *>>>
1> ]
1>Done building project "C++ Test.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
First, include the proper headers:
#include <algorithm> // fill
#include <cstddef> // size_t
#include <string> // string
#include <vector> // vector
Then don't use NULL that may be defined as a simple 0 which does not match the node* you want for std::fill. Use nullptr. You also do not need to reserve just before you resize.
class HashTable {
private:
std::vector<node*> bucket;
// std::size_t capacity; // use bucket.capacity() instead
public:
HashTable(std::size_t max_size = 100) {
bucket.resize(max_size);
std::fill(bucket.begin(), bucket.end(), nullptr);
}
std::size_t capacity() const { return bucket.capacity(); }
};
The fill operation is not necessary though since the resize operation will zero initialize all the pointers anyway.
Whilst NULL is a null pointer literal, it isn't required to be an expression of pointer type.
You could use nullptr, however the resize has done that for you, and the constructor of std::vector could have done that too.
#include <cstddef>
#include <string>
#include <vector>
struct node
{
std::string key;
std::string value;
node* next;
};
class HashTable
{
private:
std::vector<node*> bucket;
// we don't need to repeat bucket.capacity() as a data member
public:
HashTable(std::size_t max_size = 100)
: bucket(max_size)
{
// we don't need to reserve, we've constructed a vector of appropriate size
}
};
I'm doing a little file watcher in C++, but after some refactoring I got stuck with some problem. Mainly I understand what's the problem. I call a function which is actually not a function, but I cannot find such thing. All answers say that there is some name which is called as a function.
Here is my header:
#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include <unordered_map>
#include <filesystem>
namespace fs = std::filesystem;
class FileWatcher
{
size_t currentNumberOfFiles = 0;
fs::path pathToWatch;
std::unordered_map<fs::path, fs::file_time_type> pathsMap;
std::string currentTime();
public:
FileWatcher(fs::path path);
void start();
};
#endif
And the .cpp file:
#pragma warning(disable : 4996)
#include "FileWatcher.h"
#include "Event.h"
std::string FileWatcher::currentTime()
{
auto now = std::chrono::system_clock::now();
std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
std::string currentSystemTime = std::ctime(&nowTime);
return currentSystemTime;
}
FileWatcher::FileWatcher(fs::path pathToWatch)
{
this->pathToWatch = pathToWatch;
//create a map with last modification of a given file in the directory
for (auto& file : fs::directory_iterator(this->pathToWatch))
{
pathsMap.emplace(file.path(), fs::last_write_time(file));
}
}
void FileWatcher::start()
{
while (true)
{
currentNumberOfFiles = std::distance(fs::directory_iterator(pathToWatch), fs::directory_iterator());
for (std::unordered_map<fs::path, fs::file_time_type>::iterator it = pathsMap.begin(); it != pathsMap.end(); )
{
if (!fs::exists(it->first))
{
if (currentNumberOfFiles < pathsMap.size())
{
//std::cout << "File was erased" << std::endl;
it = pathsMap.erase(it);
//FileType fileType = ( ? FileType::FILE : FileType::DIRECTORY);
std::string time = this->currentTime();
Event event(EventType::DELETED, FileType::FILE, it->first, time);
event.printEvent();
}
else
{
//std::cout << "Renamed" << std::endl;
it = pathsMap.erase(it);
}
}
else
{
it++;
}
}
for (auto& file : fs::directory_iterator(pathToWatch))
{
if (!pathsMap.count(file.path()))
{
//std::cout << "File has been created" << std::endl;
pathsMap.emplace(file.path(), fs::last_write_time(file));
}
else
{
if (pathsMap.at(file.path()) != fs::last_write_time(file))
{
//std::cout << "File has been modified" << std::endl;
pathsMap.at(file.path()) = fs::last_write_time(file);
}
}
}
}
}
Here is the error list:
1st error:
Severity Code Description Project File Line Column Category Source Suppression State
Error C2056 illegal expression ProgrammingAssignment C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash 130 44 Build
2nd error:
Severity Code Description Project File Line Column Category Source Suppression State
Error C2064 term does not evaluate to a function taking 1 arguments ProgrammingAssignment C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash 131 53 Build
This is the place error list refer to in the filesystem library.
template <class _Hasher, class _Kty>
_INLINE_VAR constexpr bool _Nothrow_hash = noexcept(
static_cast<size_t>(_STD declval<const _Hasher&>()(_STD declval<const _Kty&>())));
This is the error output:
Build started...
1>------ Build started: Project: ProgrammingAssignment, Configuration: Debug Win32 ------
1>FileWatcher.cpp
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(131,53): error C2064: term does not evaluate to a function taking 1 arguments
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(155): message : see reference to variable template 'const bool _Nothrow_hash<std::hash<std::filesystem::path>,std::filesystem::path>' being compiled
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(155): message : while compiling class template member function 'size_t std::_Uhash_compare<_Kty,_Hasher,_Keyeq>::operator ()<_Kty>(const _Keyty &) noexcept(<expr>) const'
1> with
1> [
1> _Kty=std::filesystem::path,
1> _Hasher=std::hash<std::filesystem::path>,
1> _Keyeq=std::equal_to<std::filesystem::path>,
1> _Keyty=std::filesystem::path
1> ]
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(1218): message : see reference to variable template 'const bool _Nothrow_hash<std::_Umap_traits<std::filesystem::path,std::chrono::time_point<std::filesystem::_File_time_clock,std::chrono::duration<__int64,std::ratio<1,10000000> > >,std::_Uhash_compare<std::filesystem::path,std::hash<std::filesystem::path>,std::equal_to<std::filesystem::path> >,std::allocator<std::pair<std::filesystem::path const ,std::chrono::time_point<std::filesystem::_File_time_clock,std::chrono::duration<__int64,std::ratio<1,10000000> > > > >,0>,std::filesystem::path>' being compiled
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(1218): message : while compiling class template member function 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>> std::_Hash<std::_Umap_traits<_Kty,std::chrono::time_point<std::filesystem::_File_time_clock,std::chrono::duration<std::chrono::system_clock::rep,std::chrono::system_clock::period>>,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>::erase<std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,0>(std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>>) noexcept(<expr>)'
1> with
1> [
1> _Ty=std::pair<const std::filesystem::path,std::filesystem::file_time_type>,
1> _Kty=std::filesystem::path,
1> _Hasher=std::hash<std::filesystem::path>,
1> _Keyeq=std::equal_to<std::filesystem::path>,
1> _Alloc=std::allocator<std::pair<const std::filesystem::path,std::filesystem::file_time_type>>
1> ]
1>C:\VisualStudio2019\VC\Tools\MSVC\14.28.29910\include\xhash(130,44): error C2056: illegal expression
1>Done building project "ProgrammingAssignment.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The error messages are very difficult to read, but it basically boils down to a missing implementation of std::hash<std::filesystem::path>.
std::unordered_map uses hashes to order its elements. By default, it uses a specialization of std::hash on the map's key_type. However, the standard library does not provide a specialization of std::hash for std::filesystem::path, hence the errors.
So, if you want to use std::filesystem::path as the key_type of std::unordered_map, you have to either:
provide your own specialization of std::hash<std::filesystem::path>, eg:
template <>
class std::hash<fs::path>
{
public:
size_t operator()(const fs::path &path) const
{
return ... a hash of path ...;
}
};
class FileWatcher
{
...
std::unordered_map<fs::path, fs::file_time_type> pathsMap;
...
};
implement a custom class/struct with an operator() that takes a std::filesystem::path as input and returns a unique value as output. Then you can explicitly state that type in the std::unordered_map's Hash template parameter, eg:
struct MyPathHash
{
size_t operator()(const fs::path &path) const
{
return ... a hash of path ...;
}
};
class FileWatcher
{
...
std::unordered_map<fs::path, fs::file_time_type, MyPathHash> pathsMap;
...
};
Otherwise, use std::map instead. It uses operator< to order its elements, and std::filesytem::path has its own operator< implemented, eg:
...
#include <map>
class FileWatcher
{
...
std::map<fs::path, fs::file_time_type> pathsMap;
...
};
The meaning of the VC++ compiler warning C4250 'class1' : inherits 'class2::member' via dominance is clear to me. (But see here for an explanation.)
I have currently the problem that I get this warning when serializing a class hierarchy that has an abstract base class with boost::serialization (1.44.0).
Please note that my classes do not form any kind of diamond-like inheritance hierarchy that could cause this warning, but the warning is caused by the instantiation of boost::detail::is_virtual_base_of_impl<...> when serializing instances of derived classes. (Which seems to be using is_virtual_base_of from Boost.TypeTraits.)
Here is a minimal code sample to reproduce the warning on Visual Studio 2005. Note that the code should be dropped as-is into one cpp-file and it should compile.
Note also the two points in the code that I have marked by comments that trigger the warning. If BOOST_CLASS_EXPORTis not used then the warning is not triggerd, but more interestingly the warning is also not triggered, if the derived class does not use virtual inheritance! (So maybe I do not understand C4250 after all.)
// -- std includes --
#include <iostream>
#include <sstream>
#include <string>
// -- boost serialization --
#define BOOST_SERIALIZATION_DYN_LINK
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
// Base with serialization support
struct Base
{
virtual ~Base() {};
virtual void DoStuff() const {
std::cout << "Base#[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
}
template<class Archive> // serialization support!
void serialize(Archive & ar, const unsigned int file_version) { /*empty*/ }
};
// (The only) Specific class with ser. support
struct Concrete2 : virtual/*!C4250!*/ public Base
{
virtual void DoStuff() const {
std::cout << "Concrete2#[" << static_cast<const void*>(this) << "]::DoStuff() called\n";
}
template<class Archive> // serialization support!
void serialize(Archive & ar, const unsigned int ver) {
ar & boost::serialization::base_object<Base>(*this);
// This is just a test - no members neccessary
std::cout << "Concrete2::serialize!" << typeid(ar).name() << "\n";
}
};
// Without guid export -> *no* C4250, even *with* virtual inheritance
// (however, can't be serialized via base class pointer anymore)
BOOST_CLASS_EXPORT(Concrete2);
BOOST_CLASS_TRACKING(Concrete2, boost::serialization::track_never);
int main() {
using namespace std;
Concrete2 obj1;
obj1.DoStuff();
// The following test code is not neccessary to generate the warning ...
// (but is neccessary to show if base-pointer serialization works at runtime)
Base* ref1 = &obj1;
ostringstream out_buf;
boost::archive::text_oarchive out_archive(out_buf);
out_archive << ref1;
const string buf = out_buf.str();
cout << "Serialized obj:\n~~~~\n";
cout << buf;
cout << "\n~~~~~\n";
std::istringstream in_buf(buf);
boost::archive::text_iarchive in_archive(in_buf);
// Concrete2 obj2;
Base* ref2;
in_archive >> ref2;
if(ref2)
ref2->DoStuff();
delete ref2;
}
And here is the compiler warning (ugh!):
1>...\boost_library-1_44_0\boost\type_traits\is_virtual_base_of.hpp(61) : warning C4250: 'boost::detail::is_virtual_base_of_impl<Base,Derived,tag>::X' : inherits 'Concrete2::Concrete2::DoStuff' via dominance
1> with
1> [
1> Base=type,
1> Derived=Concrete2,
1> tag=boost::mpl::bool_<true>
1> ]
1> ...\boostserializewarningtest\vbc.cpp(27) : see declaration of 'Concrete2::DoStuff'
...
1> ...\boost_library-1_44_0\boost\mpl\eval_if.hpp(40) : see reference to class template instantiation 'boost::mpl::if_<T1,T2,T3>' being compiled
1> with
1> [
1> T1=boost::is_virtual_base_of<type,Concrete2>,
1> T2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1> T3=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1> ]
1> ...\boost_library-1_44_0\boost\serialization\void_cast.hpp(279) : see reference to class template instantiation 'boost::mpl::eval_if<C,F1,F2>' being compiled
1> with
1> [
1> C=boost::is_virtual_base_of<type,Concrete2>,
1> F1=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_virtual_base<Concrete2,type>>,
1> F2=boost::mpl::identity<boost::serialization::void_cast_detail::void_caster_primitive<Concrete2,type>>
1> ]
1> ...\boost_library-1_44_0\boost\serialization\base_object.hpp(68) : see reference to function template instantiation 'const boost::serialization::void_cast_detail::void_caster &boost::serialization::void_cast_register<Derived,Base>(const Derived *,const Base *)' being compiled
1> with
1> [
1> Derived=Concrete2,
1> Base=type
1> ]
...
1> ...\boost_library-1_44_0\boost\serialization\export.hpp(128) : while compiling class template member function 'void boost::archive::detail::`anonymous-namespace'::guid_initializer<T>::export_guid(boost::mpl::false_) const'
1> with
1> [
1> T=Concrete2
1> ]
1> ...\boostserializewarningtest\vbc.cpp(40) : see reference to class template instantiation 'boost::archive::detail::`anonymous-namespace'::guid_initializer<T>' being compiled
1> with
1> [
1> T=Concrete2
1> ]
The reason is in fact the is_virtual_base_of check from boost type traits. This check-construct will generate warning C4250 if the check is successful, as can be seen by this example:
...
struct base {
virtual void mf() { };
};
struct derived_normal : public base {
virtual void mf() { };
};
struct derived_virt : virtual public base {
virtual void mf() { };
};
int main() {
using namespace std;
cout << "boost::is_virtual_base_of<base, derived_normal>::value reports: ";
// The following line DOES NOT cause C4250
cout << boost::is_virtual_base_of<base, derived_normal>::value << endl;
cout << "boost::is_virtual_base_of<base, derived_virt> reports: ";
// The following line causes C4250:
cout << boost::is_virtual_base_of<base, derived_virt>::value << endl;
...
FWIW, the usage of this type-traits tool in boost serialization goes like this:
macro BOOST_EXPORT_CLASS ->
macro BOOST_CLASS_EXPORT_IMPLEMENT ->
struct guid_initializer (in export.hpp) ->
(...) void_cast.hpp / void_cast_register -> is_virtual_base_of is used here
As far as I can tell the warning is completely harmless in this case and can be prevented by wrapping the header in:
#pragma warning( push )
#pragma warning( disable : 4250 ) // C4250 - 'class1' : inherits 'class2::member' via dominance
#include ...
#pragma warning( pop ) // C4250
I am trying to write a simple STL iterator for CArray MFC class using boost iterator adaptor. This is my code:
#include <boost/iterator/iterator_adaptor.hpp>
#include <afxtempl.h>
class CArrIter : public boost::iterator_adaptor< CArrIter ,
int,
int,
boost::random_access_traversal_tag >
{
public:
CArrIter(CArray<int,int>& arr, int index = 0) : m_arr(arr)
{
this->base_reference() = index;
}
private:
friend class boost::iterator_core_access;
int dereference() const{
return m_arr.GetAt(base());
}
private:
CArray<int,int>& m_arr;
};
This compiles fine with VC9 compiler. But when I try compiling this with VC7 I get the following error:
\include\boost\iterator\iterator_traits.hpp(49)
: erro r C2039: 'difference_type' : is
not a member of
'boost::detail::iterator_traits<
Iterator>'
with
[
Iterator=int
]
\include\boost\mpl\eval_if.hpp(41) :
see refer ence to class template
instantiation
'boost::iterator_difference'
bein g compiled
with
[
Iterator=int
]
.... Some more ....
Any clues what could be wrong? I have to include some other header files? I am quite new to boost library.
I think that the second template parameter of boost::iterator_adaptor<> has to be a valid iterator type, try using int* instead of int.
It could be related to the random access behavior not having everything it needs to traverse the container. The 'iterator_adaptor requirements' section of this link might help:
Boost: Iterator Adapter
I'm not sure if int is assignable, so I wonder what would happen if you changed int to int&.
A couple more ideas:
Are you using the same version of the Boost library with both compilers?
Does making dereference() protected or public help?
Does anyone know why this will not compile? I've tried both VS 2008 and GCC 4.something and both spit out errors. It doesn't matter whether or not I'm referencing "ThisFunctionDoesNotCompile()".
I can workaround this by just passing 'InternalType' as a second template parameter to Base, but I'm still curious why this comes up as an error.
#include <iostream>
using namespace std;
class DataClass
{
public:
int m_data;
};
template<typename DerivedType>
class Base
{
public:
int ThisFunctionCompiles()
{
// No problems here.
typename DerivedType::InternalType temp;
temp.m_data = 5;
return temp.m_data;
}
// error C2039: 'InternalType' : is not a member of 'Derived<InInternalType>'
typename DerivedType::InternalType ThisFunctionDoesNotCompile()
{
return static_cast<DerivedType*>(this)->GetInternalData();
}
};
template<typename InInternalType>
class Derived : public Base<Derived<InInternalType> >
{
public:
typedef InInternalType InternalType;
InternalType GetInternalData()
{
return m_internalData;
}
private:
InternalType m_internalData;
public:
void SetInternalData( int newVal )
{
m_internalData.m_data = newVal;
}
};
int main()
{
Derived<DataClass> testDerived;
testDerived.SetInternalData( 3 );
cout << testDerived.GetInternalData().m_data << endl;
cout << testDerived.ThisFunctionCompiles() << endl;
// The compiler gives an error regardless of whether or not this is commented out.
//cout << testDerived.ThisFunctionDoesNotCompile().m_data << endl;
return 0;
}
These are the errors I get in VS 2008:
1>e:\test\generaltestprogram\generaltestprogram\main.cpp(27) : error C2039: 'InternalType' : is not a member of 'Derived<InInternalType>'
1> with
1> [
1> InInternalType=DataClass
1> ]
1> e:\test\generaltestprogram\generaltestprogram\main.cpp(35) : see reference to class template instantiation 'Base<DerivedType>' being compiled
1> with
1> [
1> DerivedType=Derived<DataClass>
1> ]
1> e:\test\generaltestprogram\generaltestprogram\main.cpp(58) : see reference to class template instantiation 'Derived<InInternalType>' being compiled
1> with
1> [
1> InInternalType=DataClass
1> ]
1>e:\test\generaltestprogram\generaltestprogram\main.cpp(27) : error C2146: syntax error : missing ';' before identifier 'ThisFunctionDoesNotCompile'
1>e:\test\generaltestprogram\generaltestprogram\main.cpp(27) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>e:\test\generaltestprogram\generaltestprogram\main.cpp(28) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>e:\test\generaltestprogram\generaltestprogram\main.cpp(28) : warning C4183: 'ThisFunctionDoesNotCompile': missing return type; assumed to be a member function returning 'int'
And these are what GCC gives me:
main.cpp: In instantiation of 'Base<Derived<DataClass> >':
main.cpp:96: instantiated from 'Derived<DataClass>'
main.cpp:119: instantiated from here
main.cpp:88: error: no type named 'InternalType' in 'class Derived<DataClass>'
At the time that the templated class Base is instantiated as a parent of the class Derived, the class Derived is not a complete type.
Since Base<Derived<DataClass> > is a parent class of Derived<DataClass>, it must be instantiated before Derived<DataClass> can be instantiated. So when the class Base<Derived<DataClass> > is built from the template, Derived<DataClass> behaves as if it were a forward declaration. And as you're probably aware, you can't reference members of incomplete types, nor can your forward-declare nested types, so you're out of luck here.
This, by the way, is why it's difficult to implement a properly covariant clone() method using templates. See here and here (mine).