Dinesh Khandelwal
Dinesh Khandelwal

Reputation: 327

Lexicographically compare two strings at compile time in C++11

I'm trying to use the str_const class inspired from https://stackoverflow.com/a/15863826/2859099

#include <cstddef>
#include <stdexcept>

class str_const
{
public:
    template<std::size_t N>
    constexpr str_const(const char(&arr)[N]) noexcept : str{arr}, len{N - 1} 
    {
    }

    constexpr char operator[](std::size_t i) const 
    {
        return i < len ? str[i] : throw std::out_of_range{""}; 
    }

    constexpr std::size_t size() const noexcept { return len; }

    constexpr operator const char*() const noexcept { return str; } 

    constexpr const char* c_str() const noexcept { return str; }

private:
    const char* const str;
    const std::size_t len;
};

Considering the restrictions on constexpr functions in C++11, how to implement the following lexicographical comparison:

constexpr bool operator<(str_const lhs, str_const rhs)
{
}

Upvotes: 2

Views: 663

Answers (2)

CygnusX1
CygnusX1

Reputation: 21808

Typically you would write a loop. Here you need to replace it with a tail recursion. Here is what I have got:

#include <iostream>
#include <type_traits>
#include <cstddef>

class str_const
{
public:
    template<std::size_t N>
    constexpr str_const(const char(&arr)[N]) noexcept : str{arr}, len{N - 1}
    {
    }

    constexpr char operator[](std::size_t i) const
    {
        return i < len ? str[i] : throw std::out_of_range{""}; 
    }

    constexpr std::size_t size() const noexcept { return len; }

    constexpr operator const char*() const noexcept { return str; }

    constexpr const char* c_str() const noexcept { return str; }

private:
    const char* const str;
    const std::size_t len;
};

namespace detail {
constexpr bool cmp(str_const lhs, str_const rhs, std::size_t idx) {
return lhs.size()<=idx ? true :
       rhs.size()<=idx ? false :
       lhs[idx]<rhs[idx] ? true :
       rhs[idx]<lhs[idx] ? false :
       cmp(lhs,rhs,idx+1);
}
}

constexpr bool operator<(str_const lhs, str_const rhs) {
return detail::cmp(lhs,rhs,0);
}

int main() {
std::cout << std::integral_constant< bool, str_const("def")<str_const("abc") >::value << std::endl;
std::cout << std::integral_constant< bool, str_const("abc")<str_const("abc") >::value << std::endl;
}

Note, I replaced your throw with 0 because what you are throwing is not a constant expression.

Upvotes: 2

T.C.
T.C.

Reputation: 137425

constexpr bool less_impl(const char* s1, std::size_t n1, const char* s2, std::size_t n2)
{
    return n2 == 0    ? false :
           n1 == 0    ? true :
           *s1 == *s2 ? less_impl(s1 + 1, n1 - 1, s2 + 1, n2 - 1) :
                        *s1 < *s2;
}

constexpr bool operator<(str_const lhs, str_const rhs)
{
    return less_impl(lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size());
}

Upvotes: 3

Related Questions