I'm trying to write a template method to handle items in an STL container. Getting details of the container is easy (and I use a std::enable_if clause to allow this template method to be called only if the container can be iterated over (detects a begin() method.)) I further need to know the data type held by the container. Here's what works:
template <typename CONTAINER>
std::string doStuff(const CONTAINER & container) {
using CONTAINER_TYPE = typename CONTAINER::value_type;
}
I can use if constexpr
within this method to do certain things if I can also determine the type of the things held in the container. Here's code that won't work but is like what I'm trying for:
template <typename CONTAINER, typename ITEM>
std::string doStuff(const CONTAINER<ITEM> & container) {
using CONTAINER_TYPE = typename CONTAINER::value_type;
using ITEM_TYPE = typename ITEM::value_type;
}
It completely makes sense why I can't invoke the method this way, but what could I do (either in invoking the method or with metaprogramming inside the method) to determine the type of the items in the container. I'd like to do it such that it is known at compile time.
(I've tried a couple permutations of decltype and invoke_result and tons of searching but nothing is quite working yet.) I've tried for example:
using ITEM_TYPE = std::invoke_result<&CONTAINER::begin>::type;
Of course, that returns an iterator type which needs to be dereferenced but '*' doesn't seem to work as expected here.
CodePudding user response:
You could use template template parameters:
template <template <class, class...> class CONTAINER, class ITEM, class... REST>
std::string doStuff(const CONTAINER<ITEM, REST...>& container) {
using CONTAINER_TYPE = ITEM; // or `typename CONTAINER<ITEM, REST...>::value_type`
// This requires SFINAE to not match `ITEM`s without `value_type`:
using ITEM_TYPE = typename ITEM::value_type;
//...
}
CodePudding user response:
You might simply add extra typename
template <typename CONTAINER>
std::string doStuff(const CONTAINER& container){
using CONTAINER_TYPE = typename CONTAINER::value_type;
using ITEM_TYPE = typename CONTAINER_TYPE::value_type;
}
Possibly with some SFINAE (I will use C 20 way)
template <typename CONTAINER>
std::string doStuff(const CONTAINER& container)
requires(
requires {
typename CONTAINER::value_type::value_type;
})
{
using CONTAINER_TYPE = typename CONTAINER::value_type;
using ITEM_TYPE = typename CONTAINER_TYPE::value_type;
}