【问题标题】:How to write a template class to wrap a primitive type or char* parameter如何编写模板类来包装原始类型或 char* 参数
【发布时间】:2020-11-06 10:50:36
【问题描述】:

我正在尝试编写一个模板类,该类将包装一个类型,该类型可以是原始类型(uint8_t 最多为浮点数)或 char* 字符串。如果该类包装了原始类型,则将其值设置为任何原始类型将执行直接转换,而将 char* 设置为 ascii 到原始类型的转换(atoi、sprintf 等)。如果它包装了一个 char*,该类应该管理一个内部 char* 缓冲区并将原语转换为 char*。

如果我将示例限制为仅 uint32_t 值,那么这是可以处理任何原始类型的模板(省略了明显的错误检查细节):

template<typename T>
class Measurement
{
public:
    Measurement();
    ~Measurement();

    void setU32Value(uint32_t value);
    void setStringValue(const char* std);

    uint32_t asU32() const;
    const char* asString() const;

private:
    T _value;
};

template<typename T>
void Measurement<T>::setU32Value(uint32_t value)
{
    _value = (T)value;
}

template<typename T>
void Measurement<T>::setStringValue(const char* value)
{
    _value = (T)::atoi(value);
}

template<typename T>
uint32_t Measurement<T>::asU32() const
{
    return (uint32_t)_value;
}

template<typename T>
const char* Measurement<T>::asString() const
{
    ostringstream oss;
    oss << _value;
    return oss.str();
}

但如果我尝试 Measurement 它将不起作用,因为它不会将 setU32Value 转换为字符串表示形式,并且 asU32() 不会将其转换回来。

任何人都可以提出一种方法来完成这项工作吗?

谢谢。

【问题讨论】:

  • 您可能专攻Measurement&lt;const char*&gt;,但您希望如何处理所有权与转换?在这种情况下使用std::string 似乎更合适。
  • 我的建议是将数据存储为纯字符串(即std::string),并对非字符串的任何内容使用转换函数。
  • 避免 C-cast,更喜欢 static_cast
  • 你不能有 T = const 任何东西,因为 _value 必须在构造时设置。以后不能通过函数设置。
  • @Jarod42 使用字符串无法解决问题 Measurement 会导致 asU32() {return (uint32_t)_value;} 无法编译。

标签: c++ c++11


【解决方案1】:

你的类绝对不应该手动管理它的字符串的生命周期。请参阅Single-responsibility principleThe rule of three/five/zeroRAII。所以字符串存储类型应该是std::string而不是char*

C++20 概念:

#include <string>
#include <concepts>
#include <type_traits>

template<typename T>
class Measurement
{
private:
    T _value{};

public:
    Measurement() = default;

    template <std::integral U> requires std::integral<T>
    void setIntValue(U value)
    {
        _value = static_cast<U>(value);
    }
    
    template <std::integral U> requires std::same_as<T, std::string>
    void setIntValue(U value)
    {
        _value = std::to_string(value);
    }

    void setStringValue(const std::string& str) requires std::integral<T>
    {
        _value = static_cast<T>(std::stoll(str));
    }

    void setStringValue(std::string str) requires std::same_as<T, std::string>
    {
        _value = std::move(str);
    }

    template <std::integral U> requires std::integral<T>
    U asInt() const
    {
        return static_cast<U>(_value);
    }

    template <std::integral U> requires std::same_as<T, std::string>
    U asInt() const
    {
        return static_cast<U>(std::stoll(_value));
    }

    std::string asString() const requires std::integral<T>
    {
        return std::to_string(_value);
    }

    std::string asString() const requires std::same_as<T, std::string>
    {
        return _value;
    }
};

使用示例:

auto test()
{
    {
        auto m = Measurement<long>{};
        m.setIntValue<int>(24);
    
        int a = m.asInt<int>();
        std::string s = m.asString();

        m.setStringValue("11");

        a = m.asInt<int>();
        s = m.asString();
    }


    {
        auto m = Measurement<std::string>{};
        m.setIntValue<int>(24);
    
        int a = m.asInt<int>();
        std::string s = m.asString();

        m.setStringValue("11");

        a = m.asInt<int>();
        s = m.asString();
    }
}

模板专业化

#include <string>
#include <type_traits>

template <class T, class Enable = void> class Measurement;

template <class T>
class Measurement<T, std::enable_if_t<std::is_integral_v<T>>>
{
private:
    T _value{};

public:
    Measurement() = default;

    template <std::integral U>
    void setIntValue(U value)
    {
        _value = static_cast<U>(value);
    }
    
    void setStringValue(const std::string& str)
    {
        _value = static_cast<T>(std::stoll(str));
    }

    template <std::integral U>
    U asInt() const
    {
        return static_cast<U>(_value);
    }

    std::string asString() const requires std::integral<T>
    {
        return std::to_string(_value);
    }
};

template <>
class Measurement<std::string>
{
private:
    std::string _value{};

public:
    Measurement() = default;

    
    template <std::integral U>
    void setIntValue(U value)
    {
        _value = std::to_string(value);
    }

    void setStringValue(std::string str)
    {
        _value = std::move(str);
    }

    template <std::integral U>
    U asInt() const
    {
        return static_cast<U>(std::stoll(_value));
    }

    std::string asString() const
    {
        return _value;
    }
};

用法和以前一样。


我不得不说这个类似乎太臃肿了。相反,我将有一个用于存储测量值的类和一个单独的实用程序来帮助在测量值和整数/字符串之间进行转换。

【讨论】:

  • 谢谢。不幸的是,我坚持使用 C++11。
  • @DaveR 模板专业化版本可以在 C++98 中完成。这就是我包含它的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 2021-02-18
  • 2021-09-11
  • 2022-07-13
相关资源
最近更新 更多