简体   繁体   中英

Compile-time strings: constructor overload precedence between `const char *` / `const char[]`

I am trying to make a compile-time string class. I took a few hints from this post . Unfortunately, I'm stuck on constructor overload precedence: the const char[] constructor is being ignored in favor of the const char* constructor. Any tips would be appreciated!

class string {
 public:
  // Can be done compile time. Works lovely! except...
  template<size_t N>
  constexpr string(const char(&char_array)[N])
      : ptr_(char_array), length_(N-1) {}

  // This override gets called instead.  I *must* keep this constructor.
  string(const char* const str)
      : ptr_(str) {
    length_ = strlen(str);
  }

  // Ugly hack. (not acceptable)
  template<size_t N>
  constexpr string(const char(&char_array)[N], double unused)
      : ptr_(char_array), length_(N-1) {}

 private:
  const char* ptr_;
  int length_;
};




constexpr const char kConstant[] = "FooBarBaz";

constexpr string kString(kConstant); // Error: constexpr variable 'kString' must be initialized by a constant expression (tries to call wrong overload)

constexpr string kString(kConstant, 1.0f); // ugly hack works.

There's lots of cool things I can do if I can make compile-time string constants.

  • string equality testing is faster on string than const char *
  • Eliminate run-time overhead of implicit conversions from const char * to string that call strlen() on compile-time constant strings.
  • Compile-time string sets that do equality testing instead of hashing for size < N. (this is multiple cpus of overhead on one application I'm looking at)

This is a bit ugly, but it should work:

template<class T, class = std::enable_if_t<std::is_same_v<T, char>>>
string(const T * const & str)
    : ptr_(str) {
  length_ = strlen(str);
}

The trick is that taking the pointer by const reference blocks array-to-pointer decay during template argument deduction, so when you pass an array, the compiler can't deduce T and the constructor is ignored.

The drawback is that this would also reject other things that are implicitly convertible to const char * .


An alternative might be to accept everything convertible to const char * , and then dispatch based on whether said thing is an array.

template<size_t N>
constexpr string(const char(&char_array)[N], std::true_type)
    : ptr_(char_array), length_(N-1) {}

string(const char * str, std::false_type)
    : ptr_(str) {
  length_ = strlen(str);
}

template<class T, class = std::enable_if_t<std::is_convertible_v<T, const char *>>>
constexpr string(T&& t) 
    : string(std::forward<T>(t), std::is_array<std::remove_reference_t<T>>()) {} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM