【问题标题】:Is there a way to optimize the operator+= for different data types有没有办法针对不同的数据类型优化 operator+=
【发布时间】:2021-02-24 08:52:25
【问题描述】:

“我不是专业人士,我只是在学习!”

我想在我的类中支持添加运算符,例如 Buffer。

class Buffer : public vector<uint8_t>
{
public:
    Buffer() = default;

    Buffer& operator += (const char* str)
    {
        auto pStr = str;
        while(*pStr)
        {
            emplace_back(*pStr);
            ++pStr;
        }
        return *this;
    }

    template<typename T>
    Buffer& operator += (const initializer_list<T>& ByteArray)
    {
        this->operator+=<vector<T>>(ByteArray);
        return *this;
    }
    template<class OtherContainer>
    Buffer& operator += (const OtherContainer& ByteArray)
    {
        std::copy(ByteArray.begin(), ByteArray.end(), std::back_inserter(*this));
        return *this;
    }

    template<typename T, size_t size>
    Buffer& operator += (const T (& arr)[ size ] )
    {
        std::copy( std::begin( arr ),  std::end( arr ), std::back_inserter(*this));
        return *this;
    }
};

我想覆盖:

int main()
{
    vector<uint8_t> vec = {48,49,50,51};
    string str = "45";
    char arr[] = {60,61,62,63};
    Buffer b;
    b += vec;
    b += str;
    b += "67";
    b += {56,57,58,59};
    b += arr;

    return 0;
}

是否可以为所有情况编写一个漂亮的模板?或者至少组合成一个“initializer_list”、“vector”、“string”?也许在某个地方您可以避免“复制”并使用“移动”?

【问题讨论】:

  • 容器和 c-array 可能会合并。

标签: c++ templates containers operator-overloading


【解决方案1】:

如果你使用 C++20,那么你可以使用requires 来尽可能减少这些功能。

class Buffer 
    : public std::vector<uint8_t>
{
public:
    Buffer() = default;

    template<typename T>
    Buffer& operator+=(const T& any)
        // Any container that has iterators or arrays.
        requires requires { std::begin(any); std::end(any); }
    {
        std::copy(std::begin(any), std::end(any), std::back_inserter(*this));
        return *this;
    }

    // This is a must if you want to allow:
    // b += {1, 2, 3};
    // And to not force the user to specify it's an initializer list:
    // b += std::initializer_list{1, 2, 3};
    template<typename T> 
    Buffer& operator+=(const std::initializer_list<T>& list)
    {
        std::copy(list.begin(), list.end(), std::back_inserter(*this));
        return *this;
    }
};

此外,第一个重载将接受 任何 具有迭代器的容器。

请注意,我没有检查容器中的类型,所以如果您执行以下操作:

struct S{};
std::vector<S> vec;
b += vec;

这会导致令人不快的错误。

要移动数据,您只需将 std::copy 替换为:

std::move(std::begin(any), std::end(any), std::back_inserter(*this));

【讨论】:

  • 使用 std::begin(any) 而不是 any.begin() 允许将 C-array 与常规容器重新组合。
  • 如果与 C 字符串一起使用,会插入 '\0',我想避免这种情况
  • @victer std::beginstd::end 都是指针,因此您可以将结束指针减 1 以消除插入 C 样式字符串的 \0。这是一个例子:godbolt.org/z/T8TMsx
  • 所有数组都可以减 1 (if constexpr (std :: is_array_v &lt;T&gt;))。如何仅检查 C 字符串? (b += "67"; // 67\0 - 减 1,but char arr[] = {60, 61, 62, 63}; b += arr; - 不减)
  • 哎呀,没注意到。然后您可以添加另一个 if 语句来检查最后一个字符是否为 \0godbolt.org/z/W3xWfj 。我可能会建议为此创建一个函数,而不是只将它们都放在那里,只是为了美化一些东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多