Qqwy
Qqwy

Reputation: 5639

How to get at a C++ Container<T>'s T if no Container::value_type is provided?

It's rather common that container templates contain a value_type typedef. This makes it easy to create other templated code, most recently concepts, which are able to extract the T if only having been given Container and not Container<T>.

However, not all containers (or other templated classes) define such a value_type, especially older ones. Is it possible to get to the contained T even without it?

I know there are tricks like "if it is an iterator then .begin() should return a value of that type", but that does not help us to e.g. write a concept-requirement that checks whether a class's .begin() indeed follows the requirements that iterators have.

Upvotes: 2

Views: 419

Answers (2)

cigien
cigien

Reputation: 60238

Here's a solution that's similar to Quimby's solution but using partial template specialization instead:

template<typename...>
struct inner_type_impl;

template<template<typename...> typename C, typename T, typename...Args>
struct inner_type_impl<C<T,Args...>>
{
    using type = T;
};

template<typename T>
using inner_type = typename inner_type_impl<T>::type;

Here's a demo that's borrowed from Quimby's solution.

Upvotes: 4

Quimby
Quimby

Reputation: 19123

Class template specialization or template argument deduction can be used to implement this. Something like the following should work as long as the inner type is the first template argument:

#include <type_traits>

// Consider the first template argument to be the inner type.
template<template<typename,typename...>class C,typename T,typename...Args>
auto inner_type_impl(const C<T,Args...>* v)->T*{return nullptr;};

template<typename T>
using inner_type = std::remove_pointer_t<decltype(inner_type_impl((T*)nullptr))>;


template<typename T>
struct Container;

// Will still deduce T
template<typename T,typename...Extras>
struct ContainerExtraParams;

static_assert(std::is_same_v<int,inner_type<Container<int>>>);
static_assert(std::is_same_v<int,inner_type<ContainerExtraParams<int,double,float>>>);

I use pointers to make the code valid in evaluated contexts too. Contrary to a possible solution involving std::declval.

Upvotes: 3

Related Questions