I just read this article on how to write a standard-like algorithm and wanted to try it myself by writing (as suggested in the article) an algorithm to test if a sequence given by two bidirectional iterators is a palindrome.
Here is my code with two different ways of calling the algorithm (is_palindrome
and alt_is_palindrome
):
#include <cctype> // std::tolower
#include <functional> // std::equal_to
#include <iostream> // std::cout
#include <iterator> // std::iterator_traits<T>::value_type
#include <string>
#include <vector>
template<typename Iterator>
using ItVType = typename std::iterator_traits<Iterator>::value_type;
template<typename Iterator>
using StdEqual= typename std::equal_to<ItVType<Iterator>>;
template<typename BidirectionalIterator, typename Compare=StdEqual<BidirectionalIterator>>
bool is_palindrome(BidirectionalIterator first, BidirectionalIterator last, Compare comp=Compare())
{
for(; first != last && first != --last; ++first)
if(not comp(*first,*last))
return false;
return true;
}
template<typename BidirectionalIterator, typename Compare>
bool alt_is_palindrome(BidirectionalIterator first, BidirectionalIterator last, Compare comp)
{
for(; first != last && first != --last; ++first)
if(not comp(*first,*last))
return false;
return true;
}
template<typename BidirectionalIterator>
inline bool alt_is_palindrome(BidirectionalIterator first, BidirectionalIterator last)
{
return alt_is_palindrome(first, last, StdEqual<BidirectionalIterator>());
}
int main()
{
std::vector<std::string> vs = { "", "palindrome", "abba", "abcBA"};
std::cout << "case sensitive:" << std::endl;
for(auto const& s : vs)
std::cout << s << " is "
<< ( is_palindrome(begin(s),end(s)) ? "" : "not " )
<< "a palindrome." << std::endl;
std::cout << std::endl;
auto my_comp = [](auto const& lhs, auto const& rhs)
{
return tolower(lhs) == std::tolower(rhs);
};
std::cout << "case insensitive:" << std::endl;
for(auto const& s : vs)
std::cout << s << " is "
<< ( is_palindrome(begin(s),end(s), my_comp) ? "" : "not " )
<< "a palindrome." << std::endl;
}
So first: I like the style of is_palindrome
better with the default arguments, but I haven't seen it used in that way before (maybe I just haven't seen enough code...).
Is there a preferred way to declare this?
If so, is it 'just' a convention or are there actual benefits (performance, security,...) to it?
The second thing I am not sure about are my range-based for
-loops and the lambda function int the main
function.
As you see I took their respective variables by auto const&
. I have sometimes seen people take them as auto&&
, but I am not quite sure about he implications of this. Is this way preferred?
If you note anything else that isn't quite right or nice (e.g. the kind of type aliases I use), please let me know, too.