【问题标题】:Compile-time strings: constructor overload precedence between `const char *` / `const char[]`编译时字符串:`const char *` / `const char[]` 之间的构造函数重载优先级
【发布时间】:2016-04-06 18:41:06
【问题描述】:

我正在尝试创建一个编译时字符串类。我从this post 那里得到了一些提示。不幸的是,我坚持构造函数重载优先级:const char[] 构造函数被 const char* 构造函数所取代。任何提示将不胜感激!

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.

如果我可以制作编译时字符串常量,我可以做很多很酷的事情。

  • string 上的字符串相等性测试比const char * 上更快
  • 消除从 const char *string 的隐式转换在编译时常量字符串上调用 strlen() 的运行时开销。
  • 编译时字符串集执行相等性测试而不是散列大小

【问题讨论】:

  • 如果您查看the std::string constructor overloads,您会发现它们甚至不关心数组,只关心指针。
  • 它们也不支持 constexpr 构造!这才是重点。模板化字符数组能够在编译时检测字符串大小。
  • 非模板函数优先于模板函数,即使它需要指针衰减。不过,我不知道如何解决这个问题。当我尝试这样做时,我自己也遇到了同样的问题,最后选择了基于可变参数模板的字符串。
  • 即使我注释掉string(const char *),此代码也无法在 gcc 5.1.0 中编译是编译器的问题吗? - 错误:'string{((const char*)(& kConstant)), 9}' 不是常量表达式
  • @Slava,为我编译...

标签: c++ string constexpr compile-time-constant constructor-overloading


【解决方案1】:

这有点难看,但应该可以:

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

诀窍是在模板参数推导期间通过 const 引用获取指针会阻止数组到指针的衰减,因此当您传递数组时,编译器无法推导出 T 并且构造函数被忽略。

缺点是这也会拒绝其他可以隐式转换为const char *的东西。


另一种方法可能是接受可转换为const char * 的所有内容,然后根据所述内容是否为数组进行分派。

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>>()) {} 

【讨论】:

  • 令人印象深刻。我尝试了类似的方法,但如果没有指针衰减技巧,它就无法工作。 +1 用于解释为什么 const &amp; 会有所作为。不幸的是,我认为我无法获得过去的代码审查,因为它使原始代码路径复杂化。
猜你喜欢
  • 2013-02-04
  • 1970-01-01
  • 1970-01-01
  • 2020-11-14
  • 2016-05-29
  • 1970-01-01
  • 2018-11-05
  • 2013-04-23
  • 1970-01-01
相关资源
最近更新 更多