I have a quick question for my program: How can I call this template function with Set, rather than int?
I have a class here called Set
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
class Set
{
public:
class Iterator;
void add(T v);
void remove(T v);
Iterator begin();
Iterator end();
private:
vector<T> data;
};
Here's my cpp:
Unfortunately, main cannot be a template function so I had to make another function addstuff, which main calls
template <class T>
Set<T> addstuff()
{
Set<T> a;
a.add(1);
a.add(2);
a.add(3);
a.add("a string");
return a;
}
void main()
{
addstuff<Set>(); //<< Error here. If I use addstuff<int>(), it would run but
//I can't add string to it. I am required to be able to add
//different data types to this vector
}
Your writing addstuff<Set>() would be an attempt to resolve to Set<Set> addstuff() which is meaningless.
addstuff<std::string>() would allow you to add std::strings to your set, but then a.add(1) would fail since the literal cannot be implicitly converted to a string type.
addstuff<int>() does work but that's a merry coincidence. add(1) has the correct type in that instance to be added to Set<int>.
You could build a class Foo that has non-explicit constructors to a string and an integer and make that your template type: addstuff<Foo>(). But I'm not convinced that's what your professor wants you to do and there are better ways of solving this (type erasure for one, but this is getting quite involved).
Related
I am running into a design issue with my code and I am not sure where to go. I am attempting to write a basic I/O class to write vectors of data into a text file for convenience in my research. In practice, I would like to not worry about the datatype in the array that I am writing to file and use the same interface regardless of if it is an array of ints, doubles, etc.
My basic idea was to create an AbstractColumn class, with a templated derived class to handle columns of different data. My I/O class could then contain an array of pointers to objects of this Abstract Class, and I can can add to this array as needed. See my header file below for the implementation of this.
#ifndef BASICIO_H
#define BASICIO_H
#include <vector>
#include <cstring>
#include <string>
#include <stdlib.h>
struct AbstractColumn{
virtual ~AbstractColumn() = 0;
template <class T> T* get_data(); // Issue comes in here, I think
int nrows=0;
};
template <class T>
class Column : public AbstractColumn {
public:
Column(std::vector<T>& v);
~Column();
T* get_data() {return data;}
T* data;
int nrows;
};
template <class T>
Column<T>::Column(std::vector<T>& v){
nrows = v.size();
data = static_cast<T*>(malloc(sizeof(T)*v.size())); //malloc returns void* so we need the static cast
std::memcpy(data, v.data(), v.size());
}
template <class T>
Column<T>::~Column() {}
class BasicIO {
public:
BasicIO(std::string outname) : fname(outname), ncols(0) {}
~BasicIO();
template <class T>
void attach(std::vector<T>& v, std::string name="");
void write();
inline int get_ncols() {return ncols;}
inline std::string filename() {return fname;}
private:
std::vector<AbstractColumn*> columns;
std::vector<std::string> column_names;
std::string fname;
int ncols;
};
template <class T>
void BasicIO::attach(std::vector<T>& v, std::string name){
Column<T>* col = new Column(v);
columns.push_back(col);
column_names.push_back(name);
}
#endif
My issue is coming in in trying to write the write() method that actually dumps this data to a file.
void BasicIO::write(){
// other formatting code here
for(int i = 0; i<columns.at(0)->nrows; i++){
for(int j = 0; j<ncols; j++){
outfile<<std::scientific<<std::left<<std::setw(col_width)<<(columns.at(j)->get_data())[i]<<" ";
} //^ this is causing an error
outfile<<std::endl;
}
outfile.close();
}
When I try to compile, I get the an error saying note: template argument deduction/substitution failed and couldn't deduce template parameter 'T'. Now, I think I understand what the issue is. When the compiler reaches this point, all it doesn't know what type of data is going to be returned by my get_data() function, since in the base class the return type is the template T. However, I don't see a way around this and I am very stuck. I have done a bit of reading and it seems like type erasure might be the way to go, but I also think I might be missing something simpler.
My Question
All of the above context aside, my concrete question is as follows: What is the best way to have an array which holds objects (in this case columns) of generic types, specifically for the application described above? I am happy to offer any other details as needed, I just felt that my code snippets were already quite long. Thank you for reading.
I have a need to implement 2 functions inside a templated class, where both functions do similar things, but not everything is the same. My proposed solution was to use if constexpr on a single template function, and then have an alias for each function:
template <typename T>
class MyClass
{
private:
template <bool test>
void TestFunc()
{
if constexpr(test)
{
// Do something
}
else
{
// Do other stuff
}
}
public:
?????? TestTrue = TestFunc<true>;
?????? TestFalse = TestFunc<false>;
}
I'm trying to figure out what should go where the question marks are, so far using, auto and const auto have not worked. I want the user to be able to call TestTrue() and TestFalse() directly from an object of the class directly.
You could do:
void TestTrue() { TestFunc<true>(); }
void TestFalse() { TestFunc<false>(); }
I don't think there's a better way.
For completeness, here's the ugly way.
As mentioned in the comments, TestFunc is a member function, not a type, so if you want to reference an explicit specialization of it, you'll need to use a member function pointer. In our case, these will be pointers of the following type.
using MemberTestFunction = void (MyClass::*)();
We can then acquire pointers to the true and false specialization of TestFunc like so:
template <typename T>
class MyClass
{
// ...
constexpr static MemberTestFunction TestTrue = &MyClass::TestFunc<true>;
constexpr static MemberTestFunction TestFalse = &MyClass::TestFunc<false>;
};
If you're not familiar with pointers to member functions, the syntax for calling TestTrue and TestFalse may look rather bizarre. If you're inside a member function, you can invoke these functions either by using the ->* operator, or by using std::invoke (C++17) from <functional>:
template <typename T>
class MyClass
{
// ...
void foo() {
// Direct call with pointer.
(this->*TestTrue)();
// Call using std::invoke.
std::invoke(TestTrue, this);
}
};
Alternatively, outside of MyClass, these calls would look like the following.
MyClass<nullptr_t> x;
// Using type deducation.
(x.*decltype(x)::TestTrue)();
// Using fully qualified name.
(x.*MyClass<nullptr_t>::TestTrue)();
// Using std::invoke (with type deducation).
std::invoke(decltype(x)::TestTrue, x);
It goes without saying this this is a needlessly obscure way of accomplishing any otherwise simple task. I would not advocate using this technique over creating new functions (as HolyBlackCat suggested) or simply naming TestFunc<true>() and TestFunc<false>() explicitly at the call site.
Transform function TestFunc to functor:
#include <iostream>
template <typename T>
class MyClass
{
private:
template <bool test>
struct TestFunc
{
void operator()() {
if constexpr(test)
{
std::cout << "TestTrue\n";
}
else
{
std::cout << "TestFalse\n";
}
}
};
public:
TestFunc<true> TestTrue;
TestFunc<false> TestFalse;
};
int main()
{
MyClass<int> myClass;
myClass.TestTrue();
myClass.TestFalse();
}
Compiling my code that contains this class:
class Dessin
{
private:
vector<Figures*>T;
public:
void ajouteFigure(const Figures& f) const
{
for(auto element: T)
{
T.push_back(f);
}
}
};
yields an error:
[Error] no matching function for call to
'std::vector::push_back(const Figures&) const'
This is what I'm supposed to do in the main()
Dessin s;
s.ajouteFigure(Cercle(1.1));
Why wouldn't this work?
Assuming Cercle is a class name, you're trying to push a value where a pointer is expected.
To "fix" the error you should change your ajouteFigure prototype to accept Figures pointers and non-const this:
void ajouteFigure(Figures* f)
Then you should call it passing a pointer to a Figures object, i.e. created with a new expression:
s.ajouteFigure(new Cercle(1.1));
That being said, this code seems pointless. You're adding the pointer as many times as you have elements in the vector (which is always 0 in the example you provided).
Using raw pointers is also unadvised, you should use smart pointers like std::unique_ptr, although that would break the current code.
Consider this, less improper, example:
class Dessin
{
private:
vector<unique_ptr<Figures>> T;
public:
void ajouteFigure(unique_ptr<Figures> f)
{
T.push_back(move(f)); // just once
}
};
and at the call site:
Dessin s;
s.ajouteFigure(make_unique<Cercle>(1.1)); // C++≥14
or, if you can't use C++14:
Dessin s;
s.ajouteFigure(unique_ptr<Figures>(new Cercle{1.1}));
Just to add to this, I think you would be better to make it a template function and create the right object inside the function with arguments to the constructor passed as function parameters.
This way you don't have to create a std::unique_ptr or use new every time you call the function.
Here's a basic implementation:
class Dessin{
public:
template<typename T, typename ... Args>
void ajouteFigure(Args &&... args){
figures.emplace_back(new T(std::forward<Args>(args)...));
}
private:
std::vector<std::unique_ptr<Figures>> figures;
};
Then using the class is less error-prone:
int main(){
Dessin d;
d.ajouteFigure<Cercle>(1.1);
}
I have the incomplete class below (but the necessary is there to understand my concern).
The following method is copying the content of a given bag (called sac in my code)
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(Sac &b) {
for(int i=0; i<b.getTaille(); i++){
*this += b.sac[i]; //LINE i DON'T UNDERSTAND
}
return *this;
}
Since the class Below has 2 attributes and a pointer to an array. In the line mentioned above what mechanism enable to take all elements in the given argument and just add it to the array of the class via *this+=b.sac[i], i would have done it in the following way
for(int i=0; i<b.getTaille(); i++){
sac[taille++]= b.sac[i];
Or perhaps there is something i don't quite understand? here is the incomplete class
template <class T, int capInitial>
class IterateurSac;
template <class T, int capInitial=64>
class Sac {
private:
T* sac;
int taille;
int capacite;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution;
void augmenterCapacite(int cap);
void copier(const Sac &b);
public:
Sac() : taille(0), capacite(capInitial), generator(7437843) {
sac= new T[capacite];
}
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(const T &element) {
if (taille==capacite)
augmenterCapacite(2*capacite);
sac[taille++]= element;
return *this;
}
}
In C++, you can create your own behaviour for operators. Your class would need to overload operator+= to make this work, and that function would name the member variable to modify.
Indeed, we can see from your first code snippet that the class is already overloading operator+= to take an argument of type Sac<T,capInitial>&; it must have another one taking an argument of type T (or compatible).
What's not clear is why the class definition you later show us does not include a declaration for either of those functions.
Here's the deal. I've looked on this forum and I didn't find the information I'm searching for or I'm probably not able to repeat it for my problem. I have a class Table which is generic and I have a class named MyString.
template <typename typeGen, int DIM>
class Table {
public:
TableauGenerique() : index_(0) { //On initialise courant à 0
}
void add(typeGen type);
private:
typeGen tableGen_[DIM];
int index_;
};
My problem is with the add function.
I sometimes have to do this in the main.cpp: (which works well)
Table <float,6> tabFloat;
tabFloat.add(1.6564);
and at one point, I need to do this which doesn't work because I need to specialize the add function to create an object of MyString, to pass it the string and then store the object in the array (tableGen) :
TableauGenerique <MyString,4> tabString;
So I tried this (after the class), without success.
template <typename typeGen, int DIM>
void Table<typeGen,DIM>::add(typeGen type){ //Which is the generic one for float or ints
if(index_ < DIM) {
tableGen_[courant_] = type;
index_++;
}
}
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type) { //(line 75) Which is the specific or specialized function for myString
MyString str(type);
if(index_ < DIM) {
tableGen_[courant_] = str;
index_++;
}
}
So, How can I make this work because it doesn't compile at all, saying: line75 : error: expected initializer before '<' token and in the main it says not matching function to call Table::add(const char[6]),
I hope everything is clear enough. Let me know if somethings is unclear.
Thank you very much for your help !
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type)
You're trying to specialize add() when in fact it is not a function template to begin with. How do you expect it to work?
You probably meant: (specialization of the class)
template <int DIM>
void Table<string,DIM>::add(string type)
But then this is allowed only if you specialize the class itself. Without specializing the class, the above code would give compilation error!
EDIT:
You can read these online tutorials:
Introduction to C++ Templates
14.5 — Class template specialization
Template Specialization and Partial Template Specialization
Explicit specialization (C++ only)
If you can control the code of the MyString class, you can provide constructors that act as implicit conversions from float to MyString. An example:
#include <string>
#include <sstream>
#include <iostream>
class MyString {
public:
MyString(float number) {
std::stringstream buffer;
buffer << number;
value = buffer.str();
}
void print() {
std::cout << value << std::endl;
}
private:
std::string value;
};
template <class T>
class Foo {
public:
void DoStuff(T item) {
item.print();
}
};
int main() {
Foo<MyString> foo;
foo.DoStuff(1.342); // implicitly converts float to MyString
return 0;
}
This way, you do not need any specialization of the add method. However, implicit conversions are tricky, and you have be careful not to invoke them accidentally, and they may create ambiguities.
EDIT: Upon a second thought, my suggestion below is basically equivalent to
Table<MyString,4> tabString;
tabString.add(MyString("whatever"));
and therefore excessive and/or does not solve the problem. Feel free to ignore :)
I would extend the class Table with a generic method to add something from which you can construct an object of the desired type:
template <typename typeGen, int DIM>
class Table {
public:
Table() : index_(0) {}
void add(typeGen type);
// The additional method
template<typename T> void add(const T& src);
private:
typeGen tableGen_[DIM];
int index_;
};
template<typename typeGen, int DIM>
template<typename T>
void Table<typeGen,DIM>::add(const T& src) {
if(index_ < DIM) {
tableGen_[courant_] = typeGen(src);
index_++;
}
}
Note construction of a temporary typeGen object before the assignment.
Assuming that MyString object can be constructed from a string literal, i.e. from const char*, you can then use it as following:
Table<MyString,4> tabString;
tabString.add("whatever");
or if the above assumption is wrong, the following should probably work (because you constructed a MyString instance from a string instance):
tabString.add(string("whatever"));