简体   繁体   中英

C++ templates: return value by type

I'm learning C++ and templates to implement a configuration file reader ( http://www.adp-gmbh.ch/cpp/chameleon.html ) and I'm trying to make a template for a class with a std::string as internal storage, which can return its internal value when assigning it to a variable (double, long double, float, string, uint16_t, uint32_t).

Platform: Win10 with Visual Studio Community 2015 Update 1

class HybridType {
public:
    HybridType() {};
    explicit HybridType(const std::string& value) : internalStr(value) {}
    explicit HybridType(const char* value) : internalStr (std::string(value)) { }

    template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
    explicit HybridType(T numericValue) {
        std::stringstream strstr;
        strstr << std::setprecision(std::numeric_limits<T>::digits10 + 1) << numericValue;
        internalStr = strstr.str();
    }

    operator std::string() const { return internalStr; }

    template<typename T>
    operator T() const {
        if (std::is_same<T, std::uint16_t>::value) return std::stoi(internalStr);
        if (std::is_same<T, std::uint32_t>::value) return std::stoul(internalStr);
        if (std::is_same<T, std::uint64_t>::value) return std::stoul(internalStr);
        if (std::is_same<T, double>::value) return std::stod(internalStr);
        if (std::is_same<T, long double>::value) return std::stold(internalStr);
        if (std::is_same<T, float>::value) return std::stof(internalStr);
        return std::stoll(internalStr);
    }



    template<typename T> operator T*() { return internalStr.c_str(); }

private:
    std::string internalStr;
};

When using it, I do the following:

uint32_t test = HybridType("50");
long double test2 = HybridType("50.0");

But when I compile this, I get a lot of warnings:

1> warning C4244: 'return': conversion from 'double' to 'uint32_t',
possible loss of data 1>  see reference to function template
instantiation 'HybridType::operator T(void) const<uint32_t>' being
compiled 1>          with 1>          [ 1>              T=uint32_t 1> ] 
1> warning C4244: 'return': conversion from 'long double' to
'uint32_t', possible loss of data 1> warning C4244: 'return':
conversion from 'float' to 'uint32_t', possible loss of data 1>
warning C4244: 'return': conversion from '__int64' to 'uint32_t',
possible loss of data

I didn't really understand why I have these warnings (compiler cannot choose the appropriate type), because when I output my variables they seem to have the correct value?

Your problem is operator T() . Look at it from the compiler's point of view. If you expand for uint32_t you get:

operator uint32_t() const {
    if (...) return std::stoi(internalStr);
    if (...) return std::stoul(internalStr);
    if (...) return std::stoul(internalStr);
    if (...) return std::stod(internalStr);
    if (...) return std::stold(internalStr);   // Consider here
    if (...) return std::stof(internalStr);
    return std::stoll(internalStr);
}

Which looks to the compiler that you might be trying to return a long double from a function that is supposed to return uint32_t . Of course, later on in the compilation process, that will get optimized down to:

operator uint32_t() const {
    return std::stoul(internalStr);
}

... but by that time, the warning has been issued.

The fix is either:

  • Write each cast operator that you want explicitly. (See R Sahu's answer for details.) This has the advantage of being simpler and less typing.
  • Write a templated cast operator declaration without a definition, and then write explicit specializations for each type that you want. (See πάντα ῥεῖ's answer for details.) This has the feature that you must create an operator for every type you want to be able tot cast too; a missing type will be a compilation error. This may be an advantage (more control on range etc), or a disadvantage (more code) depending on your application.

The advantage of the formwerapproach

Templates are evaluated at compile time, thus you can't use if() to select the appropriate conversion function at runtime (return types will conflict, hence the warnings).

You need to provide type specialized implementations for your cast operator instead:

class HybridType {
public:
    // ...
    template<typename T>
    operator T() const {
        static_assert(std::is_same<T, std::uint16_t>::value ||
                      std::is_same<T, std::uint32_t>::value 
                      // ...
                     ,"Casted to an unsupported type!");
    }
    // ...
};

template<>
HybridType::operator std::uint16_t() const {
    return std::stoi(internalStr);
}

template<>
HybridType::operator std::uint32_t() const {
    return std::stoul(internalStr);
}

// aso. ...

The answer by Martin Booner has already pointed out the core issue. I am going to suggest a simpler solution for what you want to accomplish in your class.

Using templates for classes and functions is worthwhile only when the implementation is generic. That's not true in your case. The logic to return a int is different that logic to return a double . Using a member function template for this is not the best use of templates.

template<typename T>
operator T() const {
    if (std::is_same<T, std::uint16_t>::value) return std::stoi(internalStr);
    if (std::is_same<T, std::uint32_t>::value) return std::stoul(internalStr);
    if (std::is_same<T, std::uint64_t>::value) return std::stoul(internalStr);
    if (std::is_same<T, double>::value) return std::stod(internalStr);
    if (std::is_same<T, long double>::value) return std::stold(internalStr);
    if (std::is_same<T, float>::value) return std::stof(internalStr);
    return std::stoll(internalStr);
}

is not any simpler than:

operator std::uint16_t() const {
    return std::stoi(internalStr);
}

operator std::uint32_t() const {
    return std::stoul(internalStr);
}

operator std::uint64_t() const {
    return std::stoul(internalStr);
}

operator double() const {
    return std::stod(internalStr);
}

operator long double() const {
    return std::stold(internalStr);
}

operator float() const {
    return std::stof(internalStr);
}

operator long long() const {
    return std::stoll(internalStr);
}

even though the latter is more verbose due the presence of more functions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM