I want to write a very simple std::string like compile-time const char* string.

I need to work with strings in the compiletime, just like with strings, I implemented basic functions.

class cstring final {
const char* str_;
std::size_t size_;

constexpr cstring(const char* str, std::size_t size, std::size_t prefix = 0, std::size_t suffix = 0) noexcept
    : str_{str + prefix},
        size_{size - prefix - suffix} {}

template <std::size_t N>
constexpr cstring(const char (&str)[N]) noexcept : cstring{str, N - 1, 0, 0} {}

constexpr cstring() noexcept : cstring{nullptr, 0, 0, 0} {}

cstring(const std::string& str) noexcept : cstring{str.data(), str.size(), 0, 0} {}

constexpr cstring(const cstring&) = default;

cstring& operator=(const cstring&) = default;

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

constexpr std::size_t length() const noexcept { return size_; }

constexpr std::size_t max_size() const noexcept {
    return (std::numeric_limits<std::size_t>::max)();

constexpr bool empty() const noexcept { return size_ == 0; }

constexpr const char* begin() const noexcept { return str_; }

constexpr const char* end() const noexcept { return str_ + size_; }

constexpr const char* cbegin() const noexcept { return begin(); }

constexpr const char* cend() const noexcept { return end(); }

constexpr const char& operator[](std::size_t i) const { return str_[i]; }

constexpr const char& at(std::size_t i) const {
    return (i < size_) ? str_[i]
                    : (throw std::out_of_range{"cstring::at"}, str_[0]);

constexpr const char& front() const { return str_[0]; }

constexpr const char& back() const { return str_[size_ - 1]; }

constexpr const char* data() const noexcept { return str_; }

constexpr cstring remove_prefix(std::size_t n) const {
    return {str_ + n, size_ - n};

constexpr cstring add_prefix(std::size_t n) const {
    return {str_ - n, size_ + n};

constexpr cstring remove_suffix(std::size_t n) const {
    return {str_, size_ - n};

constexpr cstring add_suffix(std::size_t n) const {
    return {str_, size_ + n};

constexpr cstring substr(std::size_t pos, std::size_t n) const {
    return {str_ + pos, n};

constexpr int compare(cstring other) const {
    return (size_ == other.size_) ? detail::StrCompare(str_, other.str_, size_)
                                : ((size_ > other.size_) ? 1 : -1);

friend constexpr bool operator==(cstring lhs, cstring rhs) {
    return lhs.compare(rhs) == 0;

friend constexpr bool operator!=(cstring lhs, cstring rhs) {
    return !(lhs == rhs);

std::string append(cstring s) const {
    return std::string{str_, size_}.append(s.str_, s.size_);

friend std::string operator+(cstring lhs, cstring rhs) {
    return std::string{lhs.str_, lhs.size_} + std::string{rhs.str_, rhs.size_};

friend std::ostream& operator<<(std::ostream& os, cstring str) {
    os.write(str.str_, str.size_);
    return os;

operator std::string() const { return std::string{str_, size_}; }

Note that I use C++11 in my project so I can't use std::string_view.

  • 2
    \$\begingroup\$ Welcome to Code Review. Could you add some additional information to your code, e.g. if you tried to reproduce the string_view interface, what you want reviewers to focus on, and so on? Also, you want to add missing #includes for std::string and std::numeric_limits. A usage example of your class would also be much appreciated. \$\endgroup\$
    – Zeta
    Mar 12, 2019 at 14:31
  • 2
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$
    – Ludisposed
    Mar 13, 2019 at 10:55
  • \$\begingroup\$ Ludisposed What should I do, roll back the changes? \$\endgroup\$
    – Neargye
    Mar 13, 2019 at 12:16
  • \$\begingroup\$ I would suggest posting a follow-up, and mentioning it in this post, one of the options listed behind ludisposed's link. You can additionally accept an answer here. \$\endgroup\$ Mar 13, 2019 at 13:06

1 Answer 1

  1. I would only ever mark polymorphic classes final. For them, there's a potential benefit to balance out the unnaturalness of not being able to inherit.

  2. You should really indent the contents of the class by one step. Though that could possibly be an artifact of only partially adapting to SE's markup.

  3. Consider verifying that prefix and suffix have sensible values, at least in debug-mode (assert()).

  4. The first ctor has sensible defaults for its last two parameters. Why not take advantage of that?

  5. Is there a reason you only accept std::string, instead of generally std::basic_string<char, std::char_traits<char>, AnyAllocator>?

  6. You could use !_size instead of _size == 0. Well, de gustibus.

  7. Your comparison is curious, but at least consistent.

  8. I would suggest conforming to C++17 std::string_view as closely as possible so you can later remove your custom class, and to ease the burden for users.

  • \$\begingroup\$ 1. I will consider. The final is a habit from Java. 2. Sorry for the indents, they disappeared during writing answer. Overlooked, correct when editing. 3. I'll add a check. 4. Do you mean to make the constructor like this? cstring() : cstring{nullptr, 0} {} 5. Yes, i think std::basic_string<char, std::char_traits<char>, AnyAllocator> will be better. 6. Ok, will be noted. 7. Oh, i forgot add detail::StrCompare to src. Will be fix soon. 8. I'll think about whether I can upgrade the project to C++17 \$\endgroup\$
    – Neargye
    Mar 12, 2019 at 16:39
  • \$\begingroup\$ re 4: Yes, that's it. \$\endgroup\$ Mar 12, 2019 at 17:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.