Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ templates: return value by type

Tags:

c++

templates

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?

like image 653
Ayak973 Avatar asked Feb 04 '26 12:02

Ayak973


2 Answers

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

like image 179
Martin Bonner supports Monica Avatar answered Feb 07 '26 02:02

Martin Bonner supports Monica


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. ...
like image 31
πάντα ῥεῖ Avatar answered Feb 07 '26 00:02

πάντα ῥεῖ