简体   繁体   中英

Converting between types in function templates

I have a function template that gives the result by reference variable that defined by template. This function must convert a value and assign to the reference variable. I have problem with compiling in the line that I show in the code.

What is wrong and how can I solve that?

The following is my code:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>

using namespace std;

map<wstring, wstring> my_data = {
    { L"data1", L"123.456"  },
    { L"data2", L"2213.323" },
    { L"data3", L"3321.321" },
    { L"data4", L"1000" },
    { L"data5", L"2000" }
};



template<class T>
bool get_map_value(const wstring &map_key, T& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    if (typeid(T).name() == typeid(wstring).name())
        map_value = tmp->second;
        //Error C2440   '=': cannot convert from 'std::wstring' to 'int'    
        //Error C2440   '=': cannot convert from 'std::wstring' to 'double' 

    else if (typeid(T).name() == typeid(double).name())
        map_value = _wtof(tmp->second.c_str());

    else if (typeid(T).name() == typeid(int).name())
        map_value = _wtoi(tmp->second.c_str());


    return true;
}


int main() {

    double d = 0.0;
    wstring w = L"";
    int i = 0;

    get_map_value(L"data1", w);
    get_map_value(L"data3", d);
    get_map_value(L"data4", i);


    return 0;
}

What is wrong

The type of tmp->second is wstring . When the template is instantiated with T = int , then the type of map_value is int& . The line

 map_value = tmp->second; 

Has pretty self explanatory error message

 Error C2440 '=': cannot convert from 'std::wstring' to 'int' 

std::wstring is not implicitly convertible to int . That is the problem. You must remember that the entire function must be well formed, even if execution cannot reach some part of it.


how can I solve that?

What you need is just a few overloads. We shall refactor the differing part of the function, so that you don't need to repeat the identical part.

parse_value(int& variable, const std::wstring& value) {
    variable = _wtoi(tmp->second.c_str());
}
parse_value(double& variable, const std::wstring& value) {
    variable = _wtof(tmp->second.c_str());
}
parse_value(std::wstring& variable, const std::wstring& value) {
    variable = value;
}

template<class T>
bool get_map_value(const wstring &map_key, T& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    parse_value(map_value, tmp->second);

    return true;

}

Template are evaluated at compile time. Run-time if statements don't protect the code being run, as the compiler can't guarantee that the path won't be taken.

As of C++17 you would be able to use constexpr if , but meanwhile, you have to write different specializations for it (or even overloads, you don't even need a template):

bool get_map_value(const wstring &map_key, wstring& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = tmp->second;

    return true;
}

bool get_map_value(const wstring &map_key, double& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = _wtof(tmp->second.c_str());    

    return true;
}

bool get_map_value(const wstring &map_key, int& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = _wtoi(tmp->second.c_str());

    return true;
}

First, remove these ugly typeid() s... And use SFINAE instead.

template <typename T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
bool get_map_value(std::wstring const& map_key, T& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = _wtof(val.second);
        return true;
    } else {
        return false;
    }
}

Similarly, you then add another definition for integral types:

template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
bool get_map_value(std::wstring const& map_key, T& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = _wtoi(val.second);
        return true;
    } else {
        return false;
    }
}

And finally a simple overload for std::wstring :

bool get_map_value(std::wstring const& map_key, std::wstring& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = val.second;
        return true;
    } else {
        return false;
    }
}

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