I have defined below hierarchy of classes, my intention is to design a generic class that allows me to iterate over enum objects (C++11 is not allowed to be used unfortunately). The class definition and test program are:
// base.h
#include <set>
template <typename T>
class Base
{
protected:
explicit Base(int value);
typedef typename std::set< Base<T>* > instances;
static instances s_instances;
int value_;
public:
int get_value() const { return value_ ; }
};
template <typename T>
Base<T>::Base(int value): value_(value)
{
s_instances.insert(this);
}
// derived.h
#include "base.h"
class Derived : public Base<Derived>
{
protected:
explicit Derived(int value): Base<Derived>(value) { }
public:
static const Derived value1;
};
// test.cc
#include "derived.h"
template<>
Base<Derived>::instances Base<Derived>::s_instances;
const Derived Derived::value1(1);
int main(int argc, char *argv[])
{
using std::cout;
using std::endl;
cout << Derived::value1.get_value() << endl;
}
On compilation using g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, it gives me below linking error:
"
g++ test.cc -o test
/tmp/ccOdkcya.o: In function `Base<Derived>::Base(int)':
test.cc:(.text._ZN4BaseI7DerivedEC2Ei[_ZN4BaseI7DerivedEC5Ei]+0x28): undefined reference to `Base<Derived>::s_instances'
collect2: ld returned 1 exit status
"
Can anyone please suggest what am I missing in above code?
Thanks!
Static data members are declared in class definitions and defined outside the class definition. Like this:
// header:
class C {
static int i;
};
// source:
int C::i = 17;
With a template, you typically don't put any code in source files, so the definition goes in the header:
// header:
template <class T>
class C {
static int i;
};
template <class T>
int C<T>::i = 17;
Be aware that s_instances might not have been initialized before it is used.
You can only write
// template<> -> you should not write this in this case.
Base<Derived>::instances Base<Derived>::s_instances;
If you have provided an explicit specialization of Base<Derived>, such as :
class Derived;
template <>
class Base<Derived>
{
protected:
explicit Base(int value);
typedef typename std::set< Base<Derived>* > instances;
static instances s_instances;
int value_;
public:
int get_value() const { return value_ ; }
};
Otherwise, you have to stick to writing:
template<typename T>
typename Base<T>::instances Base<T>::s_instances;
Related
Below is an implementation of the above question.
#include <iostream>
#include <map>
class Data {
int val;
};
template<class T>
class Test {
public:
Test(){};
private:
typename T::container _data;
};
class FunTest:public Test<FunTest> {
public:
typedef std::map<uint16_t, FunTest > container;
FunTest():Test(){}
};
int main()
{
FunTest object;
std::cout << "Hello World!\n";
}
I tried the above but I get the error
Error C2039 'container': is not a member of 'FunTest' Temp
Any insight on why this gives an error is much appreciated.
According to user17732522
Test will instantiate the template specialization before
container is declared, so T::container cannot use it.
So I guess one solution could be to move the typedef to Test class as a protected variable.
#include <iostream>
#include <map>
template<class T>
class Test {
public:
Test() {};
protected:
typedef std::map<uint16_t, T> container;
private:
typename container _data;
};
class FunTest :public Test<FunTest> {
public:
FunTest() :Test() {}
//Test<FunTest>::container c; access here as well if needed
};
int main()
{
FunTest object;
std::cout << "Hello World!\n";
}
I'm modifying a class by adding a data-member that is templated. The code will invariably be using a default template type for the template parameter in all invocations, except for one place. Partially motivated by this, and partially due to a a desire/whim, I do not want to add a template parameter to the class as shown below:
#include <iostream>
template <typename Der>
class A : private Der {
public:
int get() { return Der::get_(); }
};
class B {
protected:
int get_() { return 20; }
};
class C {
protected:
int get_() { return 30; }
};
using Default = B;
template <class T = Default>
class User {
public:
User() {}
A<T>& getMember() { return m_; }
private:
A<T> m_; // This is what I am adding, and exposing param T.
};
int main(int argc, char* argv[]) {
User h;
std::cout<<h.getMember().get()<<std::endl;
}
One solution that I could think of is the use of a sum-type, but this introduces some exception handling code:
class User {
public:
User() : m_(A<Default>()) {}
template <typename T>
User(const T& in) : m_(in) {}
template <typename T>
A<T>& getMember() { return std::get<A<T>>(m_); }
private:
std::variant<A<B>, A<C>> m_;
};
I'm looking for:
A name for what I am trying to do, if it exists.
Ways in which I can accomplish what I desire.
This is not an answer, as I don't find the virtual function call completely satisfactory, but, as progress, another option I can share is type-erasure using an inner base class.
Something like:
#include <iostream>
#include <memory>
class User {
public:
template <class T>
User(T&&) : m_(new Internal<T>()) {}
int doOnMember() { return m_->doOnMember(); }
private:
class InternalBase {
public:
virtual int doOnMember() = 0;
};
template <typename T>
class Internal : public InternalBase {
public:
Internal() {}
int doOnMember() { return m_.get(); }
private:
A<T> m_;
};
private:
std::shared_ptr<InternalBase> m_;
};
int main(int argc, char* argv[]) {
User h(B{});
std::cout<<h.doOnMember()<<std::endl;
User h1(C{});
std::cout<<h1.doOnMember()<<std::endl;
}
Consider my simple example below:
#include <iostream>
template <typename T>
class Base
{
public:
static constexpr int y = T::x;
};
class Derived : public Base<Derived>
{
public:
static constexpr int x = 5;
};
int main()
{
std::cout << Derived::y << std::endl;
}
In g++, this compiles fine and prints 5 as expected. In Clang, however, it fails to compile with the error no member named 'x' in 'Derived'. As far as I can tell this is correct code. Is there something wrong with what I am doing, and if not, is there a way to have this work in Clang?
As linked in the comments, Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class suggests that clang behaviour is standard conformant up to C++14 here. Starting with Clang 3.9, your code compiles successfully with -std=c++1z.
An easy possible workaround is to use constexpr functions instead of values:
#include <iostream>
template <typename T>
class Base
{
public:
static constexpr int y() {return T::x();}
};
class Derived : public Base<Derived>
{
public:
static constexpr int x() {return 5;}
};
int main()
{
std::cout << Derived::y() << std::endl;
}
This probably isn't the answer anyone would be looking for, but I solved the problem by adding a third class:
#include <iostream>
template <typename T>
class Base
{
public:
static constexpr int y = T::x;
};
class Data
{
public:
static constexpr int x = 5;
};
class Derived : public Base<Data>, public Data {};
int main()
{
std::cout << Derived::y << std::endl;
}
It works as desired, but unfortunately it doesn't really have the benefits of CRTP!
Ok, maybe not the best title, but here's the deal:
I have a templated interface:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
and an implementation:
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(MyStruct* aPtr)
{
} // doThis
};
However, the compiler complains:
In file included from MyTest.cpp:3:0:
MyImpl.h:7:17: error: ‘MyStruct’ has not been declared
void doThis(MyStruct* aPtr)
Why is that?
The following compiled for me:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(typename MyInterface<T>::MyStruct* aPtr)
{
}
};
int main() {
MyImpl<int> t;
}
The main change is that you need to qualify that the MyStruct was defined within MyInterface<T>.
Since the compiler cannot determine what kind of identifier is the templated subtype, you must help it using the typename keyword. (See When is the "typename" keyword necessary? for more details on typename)
I have an interface and a couple of implementations of a class that stores serialized objects. I'd like to make the implementation classes into template classes so I can use them with more than one type of object, but I'm getting compiler errors.
#include <iostream>
template<typename T>
class Interface{
public:
virtual void func(T& c) = 0;
};
class Container{
public:
Container() : dummy(10){}
int dummy;
};
template<typename T>
class Implementation : public Interface{
public:
void func(T& c){
std::cout << "++c.dummy " << ++c.dummy << std::endl;
}
};
int main(){
Container c;
Implementation<Container> i;
i.func(c);
return 0;
}
I get "error: expected class-name before ‘{’ token" at the "class Implementation..." line.
Thanks.
template<typename T>
class Implementation : public Interface<T> {
// ^^^