String comparison using general comparators - c++

I am trying to see what happens when we compare strings directly using operators like <, >, etc. The two usages in the code below surprisingly give different answers. Aren't they exactly same way of saying things?
#include <iostream>
template <class T>
T max(T a, T b)
{
//Usage 1:
if (a > b) return a; else return b;
//Usage 2:
return a > b ? a : b ;
}
int main()
{
std::cout << "max(\"Alladin\", \"Jasmine\") = " << max("Alladin", "Jasmine") << std::endl ;
}
Usage 1 gives "Jasmine" while usage 2 gives "Alladin".

When you use:
max("Alladin", "Jasmine")
it is equivalent to using:
max<char const*>("Alladin", "Jasmine")
In the function, you end up comparing pointers. The outcome of the call will depend on the values of the pointers. It is not guaranteed to be predictable.
Perhaps you want to use:
max(std::string("Alladin"), std::string("Jasmine"))
or
max<std::string>("Alladin", "Jasmine")
Be warned that some compiler might pick up std::max when you use that. You may want to change max to my_max or something like that.

You are not actually comparing the string in your code. "Alladin" and "Jasmine" are actually of the type const char[] and they decay into pointers when you call max("Alladin", "Jasmine"). This means that in your function you are comparing the address of the strings and not the contents.
If you meant to test std::strings then you need to create std::strings and pass them to your max function.

Both methods are wrong. Character strings don't have valid > operator.
You can compare std::string instead:
#include <iostream>
#include <string>
template <class T>
T my_max(T a, T b)
{
return a > b ? a : b;
}
int main()
{
std::string a = "Alladin";
std::string b = "Jasmine";
std::cout << "my max: " << my_max(a, b) << std::endl;
//standard max function:
std::cout << "standard max: " << max(a, b) << std::endl;
}
The expected result should always be "Jasmine"

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

Why is the output of the maximum of two string literals wrong?

Can someone explain, why the output is "C" in this code?
#include <iostream>
using namespace std;
template<class X>
X maximum(X a,X b)
{
if(a > b)
return a;
else
return b;
}
int main() {
cout << maximum("C","D") << endl;
}
Note that in your case the type X will be inferred as const char*, hence you are comparing two const char *s i.e. the addresses of the two string literals.
If you want to get the expected result, use something like the following
cout << maximum("C"s, "D"s) << endl;
To pass std::strings instead of passing the addresses of the string literals.
See string literal operator
Demo
Or use characters instead of using string literals i.e 'C' and 'D' and in that case, X will be inferred as char.
And See Why is "using namespace std;" considered bad practice?
When you use maximum("C","D"), the template parameter is char const*. You end up comparing two pointers. There is no guarantee which pointer will be greater. You have indeterminate behavior.
If you want to compare the string "C" and string "D", you can use:
cout << maximum(std::string("C"), std::string("D")) << endl; // or
cout << maximum("C"s, "D"s) << endl; // or
cout << maximum<std::string>("C", "D");
If you want compare just the characters C and D, you should use
cout << maximum('C', 'D') << endl;
If you want this to work for cstring-literals also, add a specialization.
#include <cstring>
template<class X>
X maximum(X a, X b)
{
return a > b ? a : b;
}
template<>
char const* maximum<char const*>(char const* a, char const* b)
{
return std::strcmp(a, b) > 0 ? a : b;
}

Program fails to call the specialized template implementation

I am trying to learn C++ template. While I run the following example, the program couldn't call the specialized template implementation. Therefore, I got the wrong output. Could anybody tell why?
template <class T>
T max(T a, T b)
{
cout << "2 ..." <<endl;
return a > b ? a : b;
}
template<>
char* max(char* a, char* b)
{
cout << "1 ..." <<endl;
return strcmp(a, b) > 0 ? a : b;
}
int main()
{
cout << max("Aladdin", "Jasmine") << endl;
return 0;
}
The arguments are passed as constant char. Therefore, try the following code instead. Note that I also included the necessary header file includes. Moreover, it is highly advisable to use std::cout, or use using std::cout;.
#include <iostream>
#include <cstring>
template <class T>
T max(T a, T b)
{
std::cout << "2 ..." << std::endl;
return a > b ? a : b;
}
template<>
const char* max(const char* a, const char* b)
{
std::cout << "1 ..." << std::endl;
return std::strcmp(a, b) > 0 ? a : b;
}
int main()
{
std::cout << max("Aladdin", "Jasmine") << std::endl;
return 0;
}
The reason you're seeing the issue is that the arguments you're passing are of type char const * (also can be spelled const char *). There isn't any standard way that I know of in C++ to print out the full name of a type. But there is a way to test this sort of thing..
One of the problems of templates is that the system will expand any templates it can. And so you will have code mysteriously work a certain way and not be completely sure why. One way you could've gotten the compiler to tell you exactly what the problem was in this case was to try to remove the template:
#include <iostream>
#include <cstring>
using ::std::cout;
using ::std::strcmp;
char* max(char* a, char* b)
{
cout << "1 ..." << '\n'; // Don't use ::std::endl Use cerr if you need flushing.
return strcmp(a, b) > 0 ? a : b;
}
int main()
{
cout << max("Aladdin", "Jasmine") << '\n';
return 0;
}
The compiler would've helpfully told you that you were trying to call a function that took non-const arguments with const arguments, and you would've learned exactly what the problem was.

I want to know the type of a variable

I was looking on the C++11 libraries, I'm not goot working with C++ objects and templates but I was trying to read the type of a variable and to run different code, but here there is an example that explains better:
if(is_scalar<typeid(list)>)
cout << list << endl;
else
for(...)
cout << list[i] << endl;
I'm trying to do something similar, I think that function is_scalar is exactly what I need cause I need to split the vectors, arrays, and lists from ints, doubles, or floats, etc.
It's hard to think of having the same variable that has different type in the same program but I'm trying to change it on runtime (still not working) or with pre-processor defines (that causes me the problem now).
I solved my problem, I founded this code that I had to modify a bit
#include <iostream>
#include <vector>
using namespace std;
template< bool B > struct Algorithm {
template<class T1> static int do_it (T1 & a) {
for (int _n(((int)((a).size()))-1), i(0); i <= _n; i++)
cout << *(next(a.begin(),i)) << endl;
cout << endl;
}
};
template<> struct Algorithm<true> {
template<class T1> static int do_it (T1 a) { cout << a << endl; }
};
template<class T1>
int elaborate (T1 A)
{
return Algorithm<std::is_scalar<T1>::value>::do_it( A ) ;
}
int main(){
int a = 42;
vector<int> b;
b.push_back(1);
b.push_back(2);
elaborate(a);
elaborate(b);
return 0;
}
At the end, decltype didn't worked because the compiler tried to "read" code that never was "used".
Did you try to use decltype? This could help you.
http://en.cppreference.com/w/cpp/language/decltype

Initialize only first argument of c++11 tuple

Minimal working example:
#include <tuple>
struct example
{
example(int, char) {}
};
int main()
{
std::tuple<example, int, double>
my_tuple(example(0, 'x'), 42, .0);
// std::tuple t = make_my_tuple(0, 'x');
return 0;
}
This works.
Is there a more elegant way to initialize only the first member, like I sketched in the comment? One which only takes the arguments to construct the first tuple member and does not initialize the others?
The reason I ask? I am just interested in the semantics of the language.
You say that giving values for the other two members is not necessary - are you worried about performance? Or that there may be no suitable value for these members?
If it's the latter, you could have your tuple hold boost::optionals. e.g.
#include <tuple>
#include <boost/optional.hpp>
using namespace boost;
struct example
{
example(int, char) {}
};
typedef std::tuple<example, optional<int>, optional<double>> MyTuple;
int main()
{
MyTuple my_tuple(example(0, 'x'), optional<int>(), optional<double>());
return 0;
}
You now semantically have the int and float "uninitialised", and can query their value as such.
To make this more elegant, you can wrap this into a function, using the perfect forwarding idiom for the arguments (in general; in this case your arguments are cheap to copy, so no speed benefit from doing this):
template <class... Args>
MyTuple make_mytuple(Args&&... args)
{
return MyTuple(example(std::forward<Args>(args)...), optional<int>(), optional<double));
}
The advantage of this template is that it's resilient to changes in example's constructor. If you add another argument, just call make_mytuple with the new arguments and it will work.
Your other point about the copying in the tuple construction is valid, but in reality I believe this will be optimal on most compilers. (a combination of RVO and elision of copies when passing an rvalue to a function by value).
You can use uniform initialization. Sadly, you cannot define a default value, argument will be initialized with the default constructor or 0.
#include <iostream>
#include <tuple>
enum class Result {Full, Partial, Empty};
std::tuple<bool, int, double> get_tuple(Result type)
{
if (type == Result::Full)
return {true, 42, 3.14159};
else if (type == Result::Partial)
return {true, 42, {}};
else
return {};
}
int main()
{
bool b;
int i;
double d;
std::tie(b, i, d) = get_tuple(Result::Full);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Partial);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Empty);
std::cout << b << " " << i << " " << d << std::endl;
return 0;
}
output:
1 42 3.14159
1 42 0
0 0 0