vendredi 28 août 2015

MSVC 2013 Bug? Retrieving last element from mapped container

Say I have a std::map<std::string, std::vector<T> and I want to write some code

that will return to me the element at the back of the mapped vector, given some key.

So I first write some code that will return to me the element at the end of a collection (vector in this case):

// get element at back of given collection
template<typename Collection>
auto ReturnLastObject(const Collection& collection) -> decltype(collection.back())&
{
    return collection.back();
}

So far so good.

Next, I realize that my function to actually do the logic will need to return the same type as ReturnLastObject would, so I write some helper struct that will allow me to use type traits to pull off the return type of a function pointer (anticipating that function pointer will be to ReturnLastObject<U>):

template<typename T>
struct GetReturnType;

template<typename Ret, typename... Args>
struct GetReturnType<Ret(*)(Args...)>
{
    using type = Ret;
};

Still doing pretty good:

Finally, I write the main function to either return the last element, or throw an exception:

template<typename MapType>
auto GetLastAddedObject(const typename MapType::key_type& key, const MapType& mapCollection)
    -> typename GetReturnType<decltype(&ReturnLastObject<typename MapType::mapped_type>)>::type&
{
    auto& objects = mapCollection.at(key);
    if (!objects.empty())
    {
        return ReturnLastObject(objects);
    }
    else
    {
        throw std::runtime_error("Could not retrieve last added item.");
    }
}

This appears to work fine in both GCC and Clang.

However, when I try compiling using MSVC 2013 (Update 3), I get the following compiler error:

error C2893: Failed to specialize function template GetReturnType<unknown-type>::type &detail::GetLastAddedObject(const MapType::key_type &,const MapType &)
        With the following template arguments:
        MapType=std::map<std::string,std::vector<int,std::allocator<_Ty>>,std::less<_Kty>,std::allocator<std::pair<const _Kty,std::vector<_Ty,std::allocator<_Ty>>>>>

Question

What I'm asking is if there is a workaround in MSVC 2013 that can accomplish the same thing, or if I just did something wrong?


Edit: MCVE

(can also be found by following the GCC and Clang links)

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <type_traits>
#include <utility>

namespace detail{
// helper struct for peeling off return type of function pointer
template<typename T>
struct GetReturnType;

template<typename Ret, typename... Args>
struct GetReturnType<Ret(*)(Args...)>
{
    using type = Ret;
};

// get element at back of given collection
template<typename Collection>
auto ReturnLastObject(const Collection& collection) -> decltype(collection.back())&
{
    return collection.back();
}

// GetLastAddedObject assumes that MapType is essentially a key mapped to a collection
// and we want to access the collection specified by the key, and return the element at the back of it
template<typename MapType>
auto GetLastAddedObject(const typename MapType::key_type& key, const MapType& mapCollection)
    -> typename GetReturnType<decltype(&ReturnLastObject<typename MapType::mapped_type>)>::type&
{
    auto& objects = mapCollection.at(key);
    if (!objects.empty())
    {
        return ReturnLastObject(objects);
    }
    else
    {
        throw std::runtime_error("Could not retrieve last added item.");
    }
}
} //end namespace detail


int main()
{
    std::map<std::string, std::vector<int>> myMap;
    myMap["StackOverflow"] = {42};

    //...
    std::string key = "StackOverflow";
    auto&& lastAddedObject = detail::GetLastAddedObject(key, myMap);
    std::cout << std::forward<decltype(lastAddedObject)>(lastAddedObject) << std::endl; // should print "42
}

Aucun commentaire:

Enregistrer un commentaire