类模板中未发生隐式转换

时间:2018-06-24 08:44:14

标签: c++ c++17

我正在编写一个基于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;
}

2 个答案:

答案 0 :(得分:3)

模板推导发生在重载解析和任何可能的参数转换之前。因此,您的操作员:

basic_sstring<CharT, SizeT> operator + (const basic_sstring<CharT, SizeT>& first_,
                                        const basic_sstring<CharT, SizeT>& second_)
除非编译器可以为两个参数推导出CharTSizeT,否则不考虑

。当然,当一个参数是文字时就不会这样。

“真实” std::stringa 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>;