自动将template< T>转换为template<const T>

4
下面的代码主要是为了一个字符串视图,目标模板实例化对象为 T={char, const char}
cmp 函数的作用是类似于 strcmp 对视图进行比较。问题在于,虽然 char* 可以轻松转换为 const char*,但我不知道如何让 SVec<char> 能够像 char* 转换为 const char* 一样容易地转换为 SVec<const char>
最后一行 (cout<<(cmp(rv, rvc));) 无法编译。我必须显式地进行转换 (cmp(SVec<const char>(rv), rvc))。是否可以像 char*const char* 的转换一样自动完成呢?
代码(大大简化):
template <typename T>
class SVec {
    protected:
        T* begin_;
        size_t size_;
    public:

        SVec(T* begin, size_t size)         : begin_(begin), size_(size)            {};
        SVec(T* begin, T* end)                  : begin_(begin), size_(end-begin) {};
        SVec(T* begin)                          : begin_(begin) { while (*(begin++)) {}; size_ = begin - 1 - begin_;    }
                                                //^null element indicates the end
        ///Conversion
        operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
};

//General lexicographic compare
template <typename T>
inline int cmp(const SVec<const T>& l, const SVec<const T> & r){
    return 1;
}

//Char specialization
template <> inline int cmp<char>(const SVec<const char>& l, const SVec<const char>& r){
    return 1;
}
//Explicit instantiation
template int cmp<char>(const SVec<const char>& l, const SVec<const char>& r);

#include <iostream>

int main(){
    using namespace std;

    char ar[] = "st";
    SVec<char> sv = ar;
    SVec<const char> svc = "str";

    cout<<(cmp(SVec<const char>(sv), svc));

    cout<<(cmp(sv, svc));
}

你的问题不在于转换,而在于推导。用户定义的转换不会被考虑用于类型推导,因此 cmp(const SVec<const T>& 无法将 const T 与行 cmp(sv, svc) 中的 char 匹配(没有 T 使得 const T 等于 char)。 - dyp
@dyp 谢谢。cmp<char>(...) 修复了它。 - Petr Skocik
@dyp,你能把那个变成一个答案吗? - Shafik Yaghmour
1个回答

3

因此,你首先应该将cmp变成一个Koenig运算符。

然后我们可以在字符和非字符版本之间进行标记调度:

template <typename T>
class SVec {
  private:
    static T* find_end(T* in) {
      // I think while(*in)++in; would be better
      // then the end is the null, not one-past-the-null.
      while(*in++) {};
      return in;
    }
  protected:
    T* begin_ = nullptr;
    size_t size_ = 0;
  public:
    SVec() = default;
    SVec(SVec const&) = default;
    SVec(T* begin, size_t size) : begin_(begin), size_(size) {};
    SVec(T* begin, T* end) : SVec(begin, end-begin) {}
    SVec(T* begin) : SVec(begin, find_end(begin)) {}
    operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
    friend int cmp(SVec<T> l, SVec<T> r) {
      return cmp_impl(l, r, std::is_same<std::decay_t<T>,char>{});
    }
  private:
    static int cmp_impl(SVec<const char> l, SVec<const char> r, std::true_type){
      return 1;
    }
    static int cmp_impl(SVec<const T> l, SVec<const T> r, std::false_type){
      return 1;
    }
  };

std::decay_tenable_if_t是C++14中的缩写版本,但它们只是没有_t后缀的typename的简短版本。

请注意,我使用按值传递而不是const&:指针和size_t不值得通过引用传递。

我还将所有构造函数转发到2个瓶颈中。

...

Koenig运算符friend int cmp使用ADL进行查找。它不是模板函数,而是为每个template类实例生成的函数,这是一个重要区别。

Koenig运算符避免了模板运算符的问题,同时允许它们根据模板类型变化。这样的运算符只能通过ADL(参数依赖查找)找到。

然后,在编译时基于T是否为char将其分派到_impl重载上(现在已经是const-correct的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接