C++ Class with Template Member Variable

C++ class with template member variable

You got very close. I added a few bits because they're handy

class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented after Parameter
template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter
};

template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};

//Here's the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{ return dynamic_cast<const Parameter<T>&>(*this).get(); }
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); }

class Diagram
{
public:
std::vector<ParameterBase*> v;
int type;
};

Diagram can then do stuff like these:

Parameter<std::string> p1("Hello");
v.push_back(&p1);
std::cout << v[0]->get<std::string>(); //read the string
v[0]->set<std::string>("BANANA"); //set the string to something else
v[0]->get<int>(); //throws a std::bad_cast exception

It looks like your intent is to store resource-owning pointers in the vector. If so, be careful to make Diagram have the correct destructor, and make it non-copy-constructable, and non-copy-assignable.

Templated member variables in C++

This is possible in the library by combining existing facilities.

The simplest implementation would be

std::unordered_map<std::type_index, std::any>

This is mildly inefficient since it stores each std::type_index object twice (once in the key and once inside each std::any), so a std::unordered_set<std::any> with custom transparent hash and comparator would be more efficient; this would be more work though.

Example.

As you say, the user of the library may not be the same as the author; in particular, the destructor of Foo does not know which types were set, but it must locate those objects and call their destructors, noting that the set of types used may be different between instances of Foo, so this information must be stored in a runtime container within Foo.

If you're wary about the RTTI overhead implied by std::type_index and std::any, we can replace them with lower-level equivalents. For std::type_index you can use a pointer to a static tag variable template instantiation (or any similar facility), and for std::any you can use a type-erased std::unique_ptr<void, void(*)(void*)> where the deleter is a function pointer:

using ErasedPtr = std::unique_ptr<void, void(*)(void*)>;
std::unordered_map<void*, ErasedPtr> member;
struct tag {};
template<class T> inline static tag type_tag;

member.insert_or_assign(&type_tag<T>, ErasedPtr{new T(value), [](void* p) {
delete static_cast<T*>(p);
}});

Example. Note that once you make the deleter of std::unique_ptr a function pointer, it is no longer default-constructible, so we can't use operator[] any more but must use insert_or_assign and find. (Again, we've got the same DRY violation / inefficiency, since the deleter could be used as the key into the map; exploiting this is left as an exercise for the reader.)

Template a member variable

You can minimize the amount of code that has to be written for each class -- it doesn't have to be a template specialization and it doesn't have to be an entire class.

class LunchBox
{
public:
std::vector<Apple> m_apples;
};

class ClassRoom
{
public:
std::vector<Student> m_students;
};

// you need one function per type, to provide the member name
auto& get_associated_vector( Student& s ) { return s.m_apples; }
auto& get_associated_vector( ClassRoom& r ) { return r.m_students; }

// and then the decorator is generic
template<typename T>
class accessor_decorator
{
T& peer;
public:
auto& getNthElement( int i ) { return get_associated_vector(peer).at(i); }

auto& takeRandomElement( int i ) { ... }

// many more ways to manipulate the associated vector

auto operator->() { return &peer; }
};

LunchBox lunchBox{};
accessor_decorator<LunchBox> lunchBoxWithAccessor{lunchBox};
auto apple3 = lunchBoxWithAccessor.getNthElement(3);

The simple helper function overload should ideally be in the same namespace as the type, to make argument-dependent lookup work (aka Koenig lookup).

It's also possible to specify the member at the point of construction, if you prefer to do that:

template<typename T, typename TMemberCollection>
struct accessor_decorator
{
// public to make aggregate initialization work
// can be private if constructor is written
T& peer;
TMemberCollection const member;

public:
auto& getNthElement( int i ) { return (peer.*member).at(i); }

auto& takeRandomElement( int i ) { ... }

// many more ways to manipulate the associated vector

auto operator->() { return &peer; }
};

template<typename T, typename TMemberCollection>
auto make_accessor_decorator(T& object, TMemberCollection T::*member)
-> accessor_decorator<T, decltype(member)>
{
return { object, member };
}

LunchBox lunchBox{};
auto lunchBoxWithAccessor = make_accessor_decorator(lunchBox, &LunchBox::m_apples);
auto apple3 = lunchBoxWithAccessor.getNthElement(3);

Derive a templated class' member variable type from a template member type

You can get the type via decltype, if the names of data member are always the same:

template <class S>
class someClass {
vector< decltype(S::variable) > history; // if S=thingA, make it a double, if S=tingB make it a complex<double>
};

C++: Can I have non-static member variable templates?

Can I have non-static member variable templates?

No.

However, you can use templates to generate a list of members like you describe. Here is an example using recursive inheritance:

template<class T, std::size_t base, std::size_t size>
class Stair;

template<class T, std::size_t base>
class Stair<T, base, base> {};

template<class T, std::size_t base, std::size_t size>
class Stair : Stair<T, base, size - 1> {
protected:
std::array<T, size> arr;
public:
template<std::size_t s>
std::array<T, s>& array() {
return Stair<T, base, s>::arr;
}
};

int main()
{
Stair<int, 2, 10> s;
auto& arr = s.array<9>();

Accessing member variables in a C# template class

You need to make an interface that contains the common properties, then have Thing1 and Thing2 inherit from it. Then, add a constraint on your type parameter <T> in GroupOfThings, and you can access the property.

public interface IThing 
{
string GroupId { get; set; }
}

public class Thing1 : IThing
{
public string Name {get; set;}
public string Id {get; set;}
public string GroupId {get; set;}
}

public class Thing2 : IThing
{
public string Size {get; set;}
public string Id {get; set;}
public string GroupId {get; set;}
}

public class GroupOfThings<T> where T : IThing
{
public GroupOfThings(List<T> things, string groupID)
{
GroupId = groupID;
Items = things;
// This is the code that I would like to be able to have
foreach(var i in Items)
{
//compiler knows about GroupId from interface
i.GroupId = groupID;
}
}

public List<T> Items;
public string GroupId;
}

Simple way to reference member variables of base class templates

As the solutions, you have to make the name value dependent to make it visible for name lookup. Besides the one you've showed, you can also:

  1. Use using to introduce the name,

    template<typename T>
    struct D1: B1<T> {
    using B1<T>::value; // or move it in method's scope according to your intent
    D1() {
    value = 1; // OK.
    }
    };
  2. Qualify with this->.

    template<typename T>
    struct D1: B1<T> {
    D1() {
    this->value = 1; // OK.
    }
    };

Access Member Variables using templates

Syntax to acces via member pointer is .* or ->*:

class Utilities {
public:
template <typename Container, typename MemberVar, typename Operator>
static void for_all(Container& C, MemberVar memvar, Operator Op)
{
for (auto& element : C) {
((*(element.*memvar)).*Op)();
}
}
};

Demo



Related Topics



Leave a reply



Submit