我正在编写一个基于std :: array的简单字符串类,如下所示。该类的行为应以非常有限的方式像std :: string一样,但是所有数据都存储在堆栈中。而且它几乎没有构造函数可以从const char *和std :: string构造。下面是代码:
#ifndef SSTRING_H
#define SSTRING_H
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <ostream>
// friend operator declarations
template <typename CharT, std::size_t SizeT>
class basic_sstring;
template <typename CharT, std::size_t SizeT>
constexpr bool operator == (const basic_sstring<CharT, SizeT>&, const basic_sstring<CharT, SizeT>&);
template <typename CharT, std::size_t SizeT>
constexpr bool operator != (const basic_sstring<CharT, SizeT>&, const basic_sstring<CharT, SizeT>&);
template <typename CharT, std::size_t SizeT>
constexpr basic_sstring<CharT, SizeT> operator + (const basic_sstring<CharT, SizeT>&,
const basic_sstring<CharT, SizeT>&);
template <typename CharT, std::size_t SizeT>
std::basic_ostream<CharT>& operator << (std::basic_ostream<CharT>&, const basic_sstring<CharT, SizeT>&);
// basic_sstring class template
template <typename CharT, std::size_t SizeT = 512>
class basic_sstring
{
public:
typedef CharT value_type;
// constructor for empty basic_sstring
constexpr basic_sstring() : _size(0)
{
_data[_size] = static_cast<CharT>(0);
}
// constructor for const char* with a size specified
constexpr basic_sstring(const CharT* data_, std::size_t size_)
: _size(size_)
{
std::memcpy(&_data[0], data_, _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// constructor for const char* which is null terminated
constexpr basic_sstring(const CharT* data_)
: _size(std::char_traits<CharT>::length(data_))
{
std::memcpy(&_data[0], data_, _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// constructor for std::string
constexpr basic_sstring(const std::basic_string<CharT>& str_)
: _size(str_.size())
{
std::memcpy(&_data[0], str_.c_str(), _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// default copy constructor
// default move constructor
// operators
// default copy assigment operator
// default move assigment operator
// comparison operators
constexpr friend bool operator == <> (const basic_sstring<CharT, SizeT>&, const basic_sstring<CharT, SizeT>&);
constexpr friend bool operator != <>(const basic_sstring<CharT, SizeT>&, const basic_sstring<CharT, SizeT>&);
// + operator
constexpr friend basic_sstring<CharT, SizeT> operator + <> (const basic_sstring<CharT, SizeT>&,
const basic_sstring<CharT, SizeT>&);
// += operator
constexpr basic_sstring<CharT, SizeT> operator += (const basic_sstring<CharT, SizeT>& other_)
{
return *this + other_;
}
// << operator
friend std::basic_ostream<CharT>& operator << <> (std::basic_ostream<CharT>&,
const basic_sstring<CharT, SizeT>&);
// reference to internal buffer
constexpr const CharT* data() const { return &_data[0]; };
// capacity
constexpr std::size_t capacity() { return SizeT; }
// size
constexpr std::size_t size() { return _size; };
private:
std::array<CharT, SizeT> _data;
std::size_t _size;
};
// == operator for basic_sstring
template <typename CharT, std::size_t SizeT>
constexpr bool operator == (const basic_sstring<CharT, SizeT>& first_,
const basic_sstring<CharT, SizeT>& second_)
{
return first_._size == second_._size && first_._data == second_._data;
}
// != operator for basic_sstring
template <typename CharT, std::size_t SizeT>
constexpr bool operator != (const basic_sstring<CharT, SizeT>& first_,
const basic_sstring<CharT, SizeT>& second_)
{
return !(first_ == second_);
}
// + operator for basic_sstring
template <typename CharT, std::size_t SizeT>
constexpr basic_sstring<CharT, SizeT> operator + (const basic_sstring<CharT, SizeT>& first_,
const basic_sstring<CharT, SizeT>& second_)
{
auto result = first_;
std::memcpy(&result._data[result._size], &second_._data[0], second_._size*sizeof(CharT));
result._size += second_._size;
result._data[result._size] = static_cast<CharT>(0);
return result;
}
// << operator for basic_sstring
template <typename CharT, std::size_t SizeT>
std::basic_ostream<CharT>& operator << (std::basic_ostream<CharT>& out,
const basic_sstring<CharT, SizeT>& sstr_)
{
std::copy(sstr_._data.begin(), sstr_._data.begin() + sstr_._size, std::ostreambuf_iterator<char>(out));
return out;
}
typedef basic_sstring<char> sstring;
#endif
但是由于某些原因,当我使用std :: string和const char *时,似乎没有发生隐式转换。我正在使用下面的代码来运行它。任何想法。
#include <iostream>
#include <string>
#include "SString.h"
int main()
{
sstring a = "First";
sstring b = a + " Second";
sstring c = a + b + " Third";
std::cout << "A = " << a << std::endl;
std::cout << "B = " << b << std::endl;
std::cout << "C = " << c << std::endl;
return 0;
}
编辑
因此,我遵循了以下r3mus n0x所描述的方法(以及随之而来的其他一些变化,例如必须在类本身中定义好友函数)。我的代码中还有其他错误。清除它们后,它可以正常工作。在下面添加可用的sstring代码和测试代码以供参考。
#ifndef SSTRING_H
#define SSTRING_H
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <ostream>
#include <type_traits>
// friend operator declarations
template <typename CharT, std::size_t SizeN>
class basic_sstring;
template <typename CharT, std::size_t SizeN>
std::basic_ostream<CharT>& operator << (std::basic_ostream<CharT>&, const basic_sstring<CharT, SizeN>&);
// basic_sstring class template
template <typename CharT, std::size_t SizeN = 512>
class basic_sstring
{
public:
typedef CharT value_type;
// constructor for empty basic_sstring
constexpr basic_sstring() : _size(0)
{
_data[_size] = static_cast<CharT>(0);
}
// constructor for const char* with a size specified
constexpr basic_sstring(const CharT* data_, std::size_t size_)
: _size(size_)
{
std::memcpy(&_data[0], data_, _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// constructor for const char* which is null terminated
constexpr basic_sstring(const CharT* data_)
: _size(std::char_traits<CharT>::length(data_))
{
std::memcpy(&_data[0], data_, _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// constructor for std::string
constexpr basic_sstring(const std::basic_string<CharT>& str_)
: _size(str_.size())
{
std::memcpy(&_data[0], str_.c_str(), _size*sizeof(CharT));
_data[_size] = static_cast<CharT>(0);
}
// default copy constructor
// default move constructor
// operators
// default copy assigment operator
// default move assigment operator
// == operator
template <typename T1, typename T2,
typename String1 = decltype(basic_sstring(std::declval<T1>())),
typename String2 = decltype(basic_sstring(std::declval<T2>()))>
constexpr friend bool operator==(const T1 & first_, const T2 & second_)
{
static_assert(std::is_same<String1, String2>::value, "Incompatable type used to compare with sstring");
auto first = String1(first_);
auto second = String2(second_);
return (first._size == second._size) &&
(std::equal(first._data.begin(), first._data.begin() + first._size, second._data.begin()));
}
// != operator
template <typename T1, typename T2,
typename String1 = decltype(basic_sstring(std::declval<T1>())),
typename String2 = decltype(basic_sstring(std::declval<T2>()))>
constexpr friend bool operator != (const T1 & first_, const T2 & second_)
{
static_assert(std::is_same<String1, String2>::value, "Incompatable type used to compare with sstring");
return !(first_ == second_);
}
// + operator
template <typename T1, typename T2,
typename String1 = decltype(basic_sstring(std::declval<T1>())),
typename String2 = decltype(basic_sstring(std::declval<T2>()))>
constexpr friend String1 operator + (const T1 & first_, const T2 & second_)
{
static_assert(std::is_same<String1, String2>::value, "Incompatable type used to add with sstring");
auto result = String1(first_);
auto second = String2(second_);
std::memcpy(&result._data[result._size], &second._data[0], second._size*sizeof(typename String1::value_type));
result._size += second._size;
result._data[result._size] = static_cast<typename String1::value_type>(0);
return result;
}
// += operator
template <typename T, typename String = decltype(basic_sstring(std::declval<T>()))>
constexpr basic_sstring<CharT, SizeN>& operator += (const T& other_)
{
static_assert(std::is_same<String, basic_sstring<CharT, SizeN> >::value,
"Incompatable type used to add with sstring");
auto other = String(other_);
std::memcpy(&_data[_size], &other._data[0], other._size*sizeof(typename String::value_type));
_size += other._size;
_data[_size] = static_cast<typename String::value_type>(0);
return *this;
}
// << operator
friend std::basic_ostream<CharT>& operator << <> (std::basic_ostream<CharT>&,
const basic_sstring<CharT, SizeN>&);
// reference to internal buffer
constexpr const CharT* data() const { return &_data[0]; };
// capacity
constexpr std::size_t capacity() const { return SizeN; }
// size
constexpr std::size_t size() const { return _size; };
// clear
constexpr void clear() { _size = 0; }
// empty
constexpr bool empty() const { return 0 == _size; }
// underlying array const
constexpr const std::array<CharT, SizeN>& underlying_array() const { return _data; }
// underlying array non const
constexpr std::array<CharT, SizeN>& underlying_array() { return _data; }
private:
std::array<CharT, SizeN> _data;
std::size_t _size;
};
// << operator for basic_sstring
template <typename CharT, std::size_t SizeN>
std::basic_ostream<CharT>& operator << (std::basic_ostream<CharT>& out,
const basic_sstring<CharT, SizeN>& sstr_)
{
std::copy(sstr_._data.begin(), sstr_._data.begin() + sstr_._size, std::ostreambuf_iterator<char>(out));
return out;
}
// User-defined deduction guides
template <typename CharT>
basic_sstring(const std::basic_string<CharT>&) -> basic_sstring<CharT, 512>;
template <typename CharT>
basic_sstring(const CharT*) -> basic_sstring<CharT, 512>;
typedef basic_sstring<char> sstring;
#endif
使用以下代码对其进行了测试:
#include <iostream>
#include <string>
#include "SString.h"
int main()
{
// Testing constructors and addition operators
sstring a = " First ";
sstring b = a + std::string(" Second ");
sstring c = a + b + " Third ";
c += c;
std::cout << "A = " << a << std::endl; // First
std::cout << "B = " << b << std::endl; // First Second
std::cout << "C = " << c << std::endl; // First First Second Third First First Second Third
// Testing comparison operators
sstring d = " Fourth ";
std::cout << "Comparison 1: " << (d == " Fourth ") << std::endl; // 1
std::cout << "Comparison 2: " << (d == std::string(" Fourth ")) << std::endl; // 1
std::cout << "Comparison 3: " << (d == sstring(" Fourth ")) << std::endl; // 1
std::cout << "Comparison 4: " << (d == sstring(" Fifth ")) << std::endl; // 0
std::cout << "Comparison 5: " << (d != " Fifth ") << std::endl; // 1
std::cout << "Comparison 6: " << (d != std::string(" Fifth ")) << std::endl; // 1
std::cout << "Comparison 7: " << (d != sstring(" Fifth ")) << std::endl; // 1
std::cout << "Comparison 8: " << (d != sstring(" Fourth ")) << std::endl; // 0
return 0;
}
答案 0 :(得分:3)
模板推导发生在重载解析和任何可能的参数转换之前。因此,您的操作员:
basic_sstring<CharT, SizeT> operator + (const basic_sstring<CharT, SizeT>& first_,
const basic_sstring<CharT, SizeT>& second_)
除非编译器可以为两个参数推导出CharT
和SizeT
,否则不考虑。当然,当一个参数是文字时就不会这样。
“真实” std::string
有a dozen overloads of operator+
可以克服这一点。
答案 1 :(得分:2)
这也是我一直在与之抗争的一件令人沮丧的事情。您必须尝试使用基于SFINAE的方法:
template <typename T1, typename T2,
typename String1 = decltype(basic_sstring(std::declval<T1>())),
typename String2 = decltype(basic_sstring(std::declval<T2>()))>
constexpr bool operator==(const T1 &string1, const T2 &string2);
它使用class template argument deduction,因此您可能需要为基于basic_string的构造函数添加用户定义的推导指南:
template <typename CharT>
basic_sstring(const std::basic_string<CharT>&) -> basic_sstring<CharT, 512>;