Custom pair class with the operator[] syntax - c++

I have this code
template<typename firstType,typename secondType>
struct bpair{
firstType first;
secondType second;
bpair(firstType firstNew,secondType secondNew) {
first = firstNew;
second = secondNew;
}
variant<firstType,secondType> operator[](size_t index) {
if(index == 0) {
return first;
}
return second;
}
};
bpair<string,int> hey = bpair(string("hi"),34);
cout << get<string>(hey[0]);
It is just a custom std::pair. Is there a way to load info from the pair using just the [] operator like this?
cout << hey[0];

A function can have only one return type. The return type cannot depend on a runtime parameter passed to the function.
Your cout << get<string>(hey[0]); selects at compile time that you want to retrieve the std::string member from the std::variant.
It is not (easily) possible to make this possible
cout << hey[0];
When hey[0] and hey[1] are supposed to call the same function.
You could let hey return some proxy type that when piped to std::cout selects the right member. Eventually, this would use again std::variant or similar under the hood (and require some boilerplate for not much gain).
For a solution that selects the member to be returned at compile time I repeat a solution suggested by Jarod42:
template <std::size_t I>
auto& operator[](std::integral_constant<std::size_t, I>) {
if constexpr {I == 0} { return first; }
else { return second; }
}
std::cout << hey[0_c] << " " << hey[1_c];
Note that hey[0_c] and hey[1_c] are calling two different instantiations of the template. Each with a different return type.

Related

How to use some type specific function (such as .size() for std::string or std::vector and so on) on something of template type?

If possible, how could I use some type-specific function (such as .size() for: std::string or std::vector or ...) in a function with a template type, being sure that when I'll use that type-specific function I'm actually calling it with the correct type as argument? Maybe I'm wrong, and if it is, please explain to me what I have to do.
#include <iostream>
#include <string>
template <typename T>
std::string func(T& number) {
if (typeid(T) == typeid(std::string)) {
unsigned short int size = number.size();// !
return " is a string";
}
else if (typeid(T) == typeid(int)) {
return " is an int";
}
//...
}
int main() {
std::string name = "Anthony";
int age = 8;
std::cout << name /*<< func(name) */<< '\n' << age << func(age) << '.';
return 0;
}
I know that in the code above the line:
unsigned short int size = number.size();//(I need exactly '.size()')
doesn't make any sense (even the whole code doesn't make much sense) considering that I never use that value, but to find the size of the string (when it is a string!) is exactly what I need, and to not post a very long code that would make sense, I'm posting only this to make it give the error I've had when trying to compile, and in order to give you a minimal reproducible example. So please, don't say to me "just delete that line and your code will work").
Instead of if (typeid(T) == typeid(std::string)), use if constexpr (std::is_same_v<T, std::string>). ( Similarly, else if constexpr instead of else if).
Regular if requires both branches to be valid, even if the condition is known at compile-time. if constexpr requires a compile-time condition, but allows the discarded branch to be invalid (only if the error is related to the template argument; every branch has to be theoretically valid for some template argument).
std::is_same_v<T, std::string> is similar to typeid(T) == typeid(std::string), except it counts as a compile-time constant. if constexpr would reject the latter.
If you really need to use a template here, simply specialize the template.
template <typename T>
std::string func(T& number);
template<>
std::string func<std::string>(std::string& number) {
unsigned short int size = number.size();// !
return " is a string";
}
template<>
std::string func<int>(int& number) {
return " is an int";;
}
Usually you using a template you want to avoid using specific implementations for types though. Overloads would be preferrable for a limited set of types using type-specific implementations though.
Since your requirement is not restricted to std::string(as you have mentioned std::vector etc), you can use SFINAE as shown below:
#include <iostream>
#include<typeinfo>
#include<string>
#include<vector>
#include <type_traits>
//make sure that this overload is added to the set when T has a size() member function which is your requirement
template<typename T>
auto func(T const& number) -> decltype((void)(number.size()), std::string())
{
auto size = number.size();
return " is a " + std::string(typeid(number).name());
}
template<typename T>
std::enable_if_t<std::is_fundamental_v<std::remove_reference_t<T>>,std::string> func(T const& number)
{
return " is a " + std::string(typeid(number).name());
}
int main()
{
std::string name = "Anthony";
int age = 8;
double dage = 22.2;
std::cout << name << func(name) << '\n' << age << func(age) << '.'<<"\n"<<dage << func(dage);
//lets test it with vector
std::vector<int> vec= {1,2,3};
std::cout<<"\nvec: "<< func(vec);
return 0;
}
Demo
The output of the above program can be seen here:
Anthony is a NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
8 is a i.
22.2 is a d
vec: is a St6vectorIiSaIiEE

Iterate throught n-dimensional vector c++

I wanted to write my own code to iterate over an n dimensional vector (where the dimension is known). Here is the code:
void printing(const auto& i, const int dimension){
int k= dimension;
for(const auto& j: i){
if(k>1){
cout<<"k: "<<k<<endl;
printing(j, --k);
}
else{
//start printing
cout<<setw(3);
cout<<j; //not quite sure about this line
}
cout<<'\n';
}
}
I get an error:
main.cpp:21:5: error: ‘begin’ was not declared in this scope
for(const auto& j: i){
^~~
Could someone help me to correct it or give me a better way to print the vector?
Thanks in advance for your time.
If the dimensions are known at compile-time, this can be solved easily with a template that takes dimensions as the non-type argument.
template <std::size_t Dimensions>
void printing(const auto& i){
if constexpr (Dimensions != 0) {
for(const auto& j: i){
// I'm not sure if it is intentional to print 'k' each iteration,
// but this is kept for consistency with the question
cout<<"k: " << Dimensions << endl;
printing<Dimensions - 1u>(j);
}
} else {
cout << setw(3);
cout << j;
cout << '\n';
}
}
The use would be, for a 2d vector:
printing<2>(vec);
Live Example
However, if you always know that const auto& i will be a std::vector type, you can potentially solve this even easier by just not using auto arguments at all, and instead use template matching:
// called only for the vector values
template <typename T>
void printing(const std::vector<T>& i){
for(const auto& j: i){
// possibly compute 'k' to print -- see below
printing(j);
}
}
// Only called for non-vector values
template <typename T>
void printing(const T& v) {
cout << setw(3);
cout << v;
cout << '\n';
}
Live Example
To compute the "dimension" of the vector, you can write a recursive type-trait for that:
#include <type_traits> // std::integral_constant
// Base case: return the count
template <std::size_t Count, typename T>
struct vector_dimension_impl
: std::integral_constant<std::size_t, Count> {};
// Recursive case: add 1 to the count, and check inner type
template <std::size_t Count, typename T, typename Allocator>
struct vector_dimension_impl<Count, std::vector<T,Allocator>>
: vector_dimension_impl<Count + 1u, T> {};
// Dispatcher
template <typename T>
struct vector_dimension : vector_dimension_impl<0u, T> {};
// Convenience access
template <typename T>
inline constexpr auto vector_dimension_v = vector_dimension<T>::value;
// Simple tests:
static_assert(vector_dimension_v<std::vector<int>> == 1u);
static_assert(vector_dimension_v<std::vector<std::vector<int>>> == 2u);
static_assert(vector_dimension_v<std::vector<std::vector<std::vector<int>>>> == 3u);
Live Example
With the above recursive trait, you can get the "dimension" of each templated vector type, without requiring the user to pass in the value at all.
If you still wanted to print k: each time, you can use the above simply with:
cout << "k: " << vector_dimension_v<T> << endl;
This only works if the type is known to be a vector -- but it could be written using concepts to work with anything following the abstract definition of something like a vector as well.
If you want this to work with any range-like type, then you could replace the vector-overload with a requires(std::ranges::range<T>) instead, and change the template-specializations for finding the dimension to also use the same. I won't pollute the answer with all this code since it's largely the same as above -- but I'll link to it in action below:
Live Example
I have made a function that can print any n-dimensional iterable container:
template<typename Object, typename Iterable>
void Print(
const Iterable& iterable,
const string& separatorDimensions = "\n",
const function<void(const Object&)>& funcPrintElem = [] (const Object& obj) {
static_assert(
is_arithmetic_v<Object> || is_same_v<remove_const_t<remove_pointer_t<Object>>, char>,
R"(The object from the innermost range is not a built-in/c-string type, please provide a valid print element function.)"
);
cout << obj << ' ';
}
) {
if constexpr (ranges::range<Iterable>) {
ranges::for_each(iterable, [&] (const auto& it) { Print(it, separatorDimensions, funcPrintElem); });
cout << separatorDimensions;
} else {
funcPrintElem(iterable);
}
}
The function has a default std::function that can print any built-in type like int, unsigned char, long long etc... and the c-string like char* or const char*, if you have another object like a pair or tuple or an object of your class you can pass a function that prints your object.
You can use the function like this: (you must explicitly tell the function your inner most object like below)
int main() {
cout << "v: " << endl;
vector<uint16_t> v { 1, 2, 3 };
Print<uint16_t>(v);
cout << endl << "ll: " << endl;
list<list<const char*>> ll { { "a", "b" }, { "c", "d" } };
Print<const char*>(ll);
struct smth {
int a;
char b;
};
cout << endl << "smths: " << endl;
vector<smth> smths { { 14, '0' }, { 18, '1' } };
Print<smth>(smths, "\n", [] (const smth& obj) { cout << "a = " << obj.a << ", b = " << obj.b << endl; });
return 0;
}
The function can be found here, maybe I will update in the future to support more things.
Edit: You need to have at least c++20 for this function to work

C++ Partially specializing parameter in fuction

As a way of learning and understanding templates in C++, we have to solve a rather simple problem with templates. Currently my program is structured as followed (minus the unrelated methods):
"LFO.h"
enum Behaviors {LIFO, FIFO};
template<typename DataType, Behaviors Behavior, int MaxElems = 10>
class LFO {
public:
bool Palindrome(LFO& p);
}
"StringFIFO.h"
template<int MaxElems = 10>
class LFO<std::string, FIFO, MaxElems> {
public:
bool Palindrome(LFO& p) {
std::cout << "FIFO Palindrome." << std::endl;
return true;
}
}
"StringLIFO.h"
template<int MaxElems = 10>
class LFO<std::string, LIFO, MaxElems> {
public:
bool Palindrome(LFO& p) {
std::cout << "LIFO Palindrome." << std::endl;
return true;
}
}
The problem is.. When I attempt to access the Palindrome(LFO&) function:
auto myStringFIFO = new LFO<string, FIFO, 5>();
auto myStringLIFO = new LFO<string, LIFO, 5>();
myStringFIFO->Palindrome(*myStringLIFO);
It says that the Palindrome requires a LFO<std::string, 1, 5> which is a FIFO instead of LFO<std::string, 0, 5> which is the LIFO that I wish to pass into the method.
When I attempt to call the Palindrome(LFO&) method on my LIFO instead, the exact opposite happens. Somehow "LFO&" becomes specified according to which specialized class I call it from.
I'm quite stuck on how to fix this. I've attempted adding a template to the method in the base class, which fixes the error in my IDE, but throws an error that no definition for the method is found.
It's pretty hard to explain correctly as I'm not very familiar to templates.
Within a template specialization (primary or not), the name of the template is short hand for this particular instance of the template.
bool Palindrome(LFO& p) {
std::cout << "LIFO Palindrome." << std::endl;
return true;
}
so this LFO is this specific LFO specialization.
template<class T, SubB, int SubE>
bool Palindrome(LFO<T,SubB, SubE>& p) {
std::cout << "LIFO Palindrome." << std::endl;
return true;
}
this is a function that takes a templated type, behavior and max size.
template<SubB, int SubE>
bool Palindrome(LFO<std::string,SubB, SubE>& p) {
std::cout << "LIFO Palindrome." << std::endl;
return true;
}
this one doesn't template the type, but does template the behavior and max elements.

How to determine the type of a passed vector to a template function

Hi guys maybe you can help me out on this,
I've two vectors (vecInt, vecDouble). Obviously one is of sort int and the other is of sort double.
But how do I check the types of those vectors in the if and the else if?
if (TYPE OF VECTOR == INT) {
std::cout << "vecInt: ";
}
else if (TYPE OF VECTOR == DOUBLE) {
std::cout << "vecDouble: ";
}
But how do I check the types of those vectors in the if and the else if?
You don't. That is not to say you can't, only that you shouldn't. This sort of branching is not going to give you the solution you want, 9 out of 10 times. A superior choice is overloading. Instead of adding a branch, add a call to a helper function, one that you then overload to get the behaviors you want.
It would look like this:
#include <vector>
#include <iostream>
template<typename T>
void output_helper(std::vector<T>*) {}
void output_helper(std::vector<int>*) {
std::cout << "vecInt: ";
}
void output_helper(std::vector<double>*) {
std::cout << "vecDouble: ";
}
template <typename T>
void output(std::vector<T>* vO) {
output_helper(vO);
for (size_t i = 0; i < vO->size(); i++) {
std::cout << (*vO).at(i) << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<int> v{1, 2, 3};
output(&v);
return 0;
}
Which indeed outputs
vecInt: 1 2 3
As you can see live. A major bonus to overloading is that you can extend the behavior of output without modifying it. Simply add an overload for another vector type.
And by the way, consider ditching the pass by pointer, and pass by reference, as one would in idiomatic C++.
You can use type traits in C++11 and C++14:
#include <type_traits>
if (std::is_same<T, int>::value) {
std::cout << "vecInt: ";
}
else if (std::is_same<T, double>::value) {
std::cout << "vecDouble: ";
}
Note that this is a runtime check, but the compiler should be able to optimize it away.
In C++17, you can instead use if constexpr, which makes this a guaranteed compile-time check with no runtime overhead, and you can also use the _v version of is_same in order to not have to write ::value every time:
if constexpr (std::is_same_v<T, int>) {
std::cout << "vecInt: ";
}
else if constexpr (std::is_same_v<T, double>) {
std::cout << "vecDouble: ";
}
In practice, however, even the former version should end up with no runtime check, as the compiler will optimize away the branches, as the if clauses are compile-time constant expressions. In each template specialization, the compiler can see that one of the branches can never be taken, and will therefore delete the branch.

How to insert a variable type as a parameter function?

I have the following function:
int readAndCheck () {
int n;
while (n < 2) {
try {
cout << "Please insert an integer bigger than one: ";
string check = "";
cin >> check;
n = boost::lexical_cast<int>(check);
} catch (boost::bad_lexical_cast&) {
cout << "Error!\n";
}
}
return n;
}
I want to generalize the function so that it can output any request to the user and check for any type. But I know the following is not possible:
checktype readAndCheck (string request, checktype) {
checktype n;
while (n < 2) {
try {
cout << request;
string check = "";
cin >> check;
n = boost::lexical_cast<checktype>(check);
} catch (boost::bad_lexical_cast&) {
cout << "Error!\n";
}
}
return n;
}
How can I create a variable that holds a type like checktype (is it possible)?
What's the way around to create a generalized readAndCheck function for every possible value of checktype?
I'm asking this because it seems tedious to have to create a function for each checktype.
SOLUTION:
#include <iostream>
#include <boost/lexical_cast.hpp>
template<typename CheckType>
CheckType readAndCheck(std::string request, CheckType n)
{
try {
std::cout << request;
std::string check = "";
std::cin >> check;
n = boost::lexical_cast<CheckType>(check);
} catch (boost::bad_lexical_cast&) {
std::cout << "Error!\n";
}
return n;
}
int main()
{
int a = 0;
while (a < 2)
a = readAndCheck("Please insert a integer bigger than one: ", a);
std::cout << a << "\n";
return 0;
}
One of the several pillars of C++ is a feature called templates, and one of their (many) primary reasons for existing is precisely to ease situations like this, where otherwise very repetitive code would be required.
Documentation is more than abundant, but in brief, the idea is you declare a templated class or function, with generic types or integral (including pointers) template arguments as placeholders for real types or values. The template is not a type until you instantiate this template with all required arguments, creating an actual type/code from it.
You instantiate a template through a declaration for a class ( e.g. MyTemplate<Arguments> myInstance{ctor_args} ), or for functions, a declaration or call (e.g. myFunc<OptionalArguments>(func_args); see also below). The arguments you supply during instantiation are substituted (in a far more sophisticated way than macros!) for the placeholders specified earlier, finally creating a concrete template class (and associated code) or function, which contains or acts upon the specified types/integral values.
For more complex cases, templates can be explicitly specialised, to act differently for different sets of template arguments. That is, you could specify one generic template that would handle most types, but explicitly specialise for a given set of arguments if other code is required there.
Template functions often don't need you to explicitly specify the template arguments, if those can be unambiguously inferred from the call site (i.e. by the type of a function argument). If template arguments cannot be deduced (e.g. there's no corresponding function argument) then you must supply them when calling, in <Brackets> like you would when instantiating a template class.
I can't test code right now (nor do I know how boost::lexical_cast works), but your simple case would be something like this:
// Return a new instance - can't deduce type, must specify at call site
// Call like: auto checked{ readAndCheck<YourType>(yourRequest) };
template<typename T_CheckType>
T_CheckType readAndCheck(std::string request)
{
// assumes T_CheckType not initialised with gibberish:
T_CheckType n;
while (n < 2) { // assumes T_CheckType convertible to int
try {
std::cout << request;
std::string check = "";
std::cin >> check;
n = boost::lexical_cast<T_CheckType>(check);
} catch (boost::bad_lexical_cast&) {
std::cout << "Error!\n"; // may return invalid instance
}
}
return n;
}
// XOR
// Write result to passed output parameter - enables deduction from arg #2
// Call like: readAndCheck(yourRequest, yourOutputParameter);
template<typename T_CheckType>
void readAndCheck(std::string request, T_CheckType &output_checked)
{
// reinitialise output_checked here if required
while (output_checked < 2) { // assumes T_CheckType convertible to int
try {
std::cout << request;
std::string check = "";
std::cin >> check;
output_checked = boost::lexical_cast<T_CheckType>(check);
} catch (boost::bad_lexical_cast&) {
std::cout << "Error!\n";
}
}
}
Otherwise all that documentation about learning templates should help you out ;-)