is_instantiation_of type trait

269 views
Skip to first unread message

Curious

unread,
Feb 20, 2017, 5:31:29 PM2/20/17
to ISO C++ Standard - Future Proposals
I have seen some problems in the past associated with passing objects of classes that are instantiations of templates with multiple parameter with one or more than one default parameters.  For example, if I have the following function

void func(const std::vector<int>& vec) { ... }

I cannot call it like so 

auto vec = std::vector<int, MyAllocator>{};
func(vec);

Because the type of the second parameter (i.e. the allocator) is different.  Maybe the following type trait can be useful for situations like this?

template <typename Type, template <typename...> class InstantiationType>
struct is_instantiation_of : std::integral_constant<bool, false> {};

template <template <typename...> class Thing, typename... Args>
struct is_instantiation_of<Thing<Args...>, Thing> : std::integral_constant<bool, true> {};

This way one could write a function that accepts any type of vector like this (without concepts)

template <typename Type, template <typename...> class Thing>
using EnableIfIsInstantiationOf = std::enable_if_t<is_instantiation_of<Type, Thing>::value>;

template <typename Vector, EnableIfIsInstantiationOf<Vector, std::vector>* = nullptr>
void func(const Vector& vec) { ... }

Brian Bi

unread,
Feb 20, 2017, 5:36:20 PM2/20/17
to std-pr...@isocpp.org

Sorry maybe I'm missing something but why can't you just do this?

template <class T, class Alloc>
void func(const std::vector<T, Alloc>& vec) { ... }

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/66dcaca8-65d4-4575-8d6a-764c8d45279b%40isocpp.org.



--
Brian Bi

Curious

unread,
Feb 20, 2017, 5:41:37 PM2/20/17
to ISO C++ Standard - Future Proposals
I feel like it would be a much more readable alternative than that because the solution of including the template parameters becomes a lot more complicated when there are multiple arguments, and each have two-three "hidden" template parameters.

Also I feel like it would help people write good specialized functions, since there are a lot of functions that can be written to accept types with different interfaces but do the same logical thing, for example, you can think of a binary search function that would linearly iterate if a linked list would be passed and randomly jump around when a vector is passed.  There are better iterator based solutions to the example I gave but I feel like it conveys what I have in mind!


On Monday, February 20, 2017 at 5:36:20 PM UTC-5, Brian Bi wrote:
On Mon, Feb 20, 2017 at 2:31 PM, Curious <aa...@umich.edu> wrote:
I have seen some problems in the past associated with passing objects of classes that are instantiations of templates with multiple parameter with one or more than one default parameters.  For example, if I have the following function

void func(const std::vector<int>& vec) { ... }

I cannot call it like so 

auto vec = std::vector<int, MyAllocator>{};
func(vec);

Because the type of the second parameter (i.e. the allocator) is different.  Maybe the following type trait can be useful for situations like this?

template <typename Type, template <typename...> class InstantiationType>
struct is_instantiation_of : std::integral_constant<bool, false> {};

template <template <typename...> class Thing, typename... Args>
struct is_instantiation_of<Thing<Args...>, Thing> : std::integral_constant<bool, true> {};

This way one could write a function that accepts any type of vector like this (without concepts)

template <typename Type, template <typename...> class Thing>
using EnableIfIsInstantiationOf = std::enable_if_t<is_instantiation_of<Type, Thing>::value>;

template <typename Vector, EnableIfIsInstantiationOf<Vector, std::vector>* = nullptr>
void func(const Vector& vec) { ... }

Sorry maybe I'm missing something but why can't you just do this?

template <class T, class Alloc>
void func(const std::vector<T, Alloc>& vec) { ... }

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.



--
Brian Bi

ric...@oehli.at

unread,
Feb 20, 2017, 5:42:08 PM2/20/17
to ISO C++ Standard - Future Proposals
This would work fine in this case.
You couldn't have forwarding references with this approach. For that you'd need some of this instance_of SFINAE trickery.

-Richard

Curious

unread,
Feb 20, 2017, 5:46:14 PM2/20/17
to ISO C++ Standard - Future Proposals, ric...@oehli.at
Hey Richard, 

Could you give me an example of what you mean?  I can try finding a solution to that and including that in the original solution.  

Thanks!

Thiago Macieira

unread,
Feb 20, 2017, 11:58:32 PM2/20/17
to std-pr...@isocpp.org
On segunda-feira, 20 de fevereiro de 2017 14:41:37 PST Curious wrote:
> Also I feel like it would help people write good specialized functions,
> since there are a lot of functions that can be written to accept types with
> different interfaces but do the same logical thing, for example, you can
> think of a binary search function that would linearly iterate if a linked
> list would be passed and randomly jump around when a vector is passed.
> There are better iterator based solutions to the example I gave but I feel
> like it conveys what I have in mind!

Isn't that what Concepts are for?

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Curious

unread,
Feb 21, 2017, 12:04:49 AM2/21/17
to ISO C++ Standard - Future Proposals
The way I understood it, concepts would still need a backend or be built on top of type traits and SFINAE, is this not correct?

Nicol Bolas

unread,
Feb 21, 2017, 12:20:57 AM2/21/17
to ISO C++ Standard - Future Proposals, aa...@umich.edu
On Monday, February 20, 2017 at 5:41:37 PM UTC-5, Curious wrote:
I feel like it would be a much more readable alternative than that because the solution of including the template parameters becomes a lot more complicated when there are multiple arguments, and each have two-three "hidden" template parameters.

Also I feel like it would help people write good specialized functions, since there are a lot of functions that can be written to accept types with different interfaces but do the same logical thing, for example, you can think of a binary search function that would linearly iterate if a linked list would be passed and randomly jump around when a vector is passed.

... that makes no sense. A binary search function should be a binary search. So it makes absolutely no sense for a binary search algorithm to work on types that binary searches can't work on.

And it also doesn't make sense to have some kind of "omni-search" function, because binary search only works if the container is sorted. When is not something you can determine from its type.

Curious

unread,
Feb 21, 2017, 12:26:37 AM2/21/17
to ISO C++ Standard - Future Proposals, aa...@umich.edu
Well you can't really binary search a linked list can you?  If you do try and do that, it would degrade to worse than linear time. 

Also when someone says binary search a range I think its safe to assume that the range is already sorted. 

"When is not something you can determine from its type."  -- It's not something you should be determining at runtime either, no binary search function should start by inspecting the range to see if it's already sorted. 

Nicol Bolas

unread,
Feb 21, 2017, 1:02:55 AM2/21/17
to ISO C++ Standard - Future Proposals, aa...@umich.edu
On Tuesday, February 21, 2017 at 12:26:37 AM UTC-5, Curious wrote:
Well you can't really binary search a linked list can you?  If you do try and do that, it would degrade to worse than linear time.

Right. Which is why `std::binary_search` and `std::equal_range` require random access iterators.

By permitting users to call these functions with the wrong kinds of types at all, you are declaring such calls to be legitimate. Even if they're slow, you're declaring that they are legitimate uses of the API.

Also when someone says binary search a range I think its safe to assume that the range is already sorted. 

"When is not something you can determine from its type."  -- It's not something you should be determining at runtime either, no binary search function should start by inspecting the range to see if it's already sorted.

Right. Which is precisely why you have different functions for linear search and binary search. The binary search should be a binary search and the linear search should be a linear search.

The algorithm that gets used should not be based on the type you pass.

Thiago Macieira

unread,
Feb 21, 2017, 1:16:05 AM2/21/17
to std-pr...@isocpp.org
On segunda-feira, 20 de fevereiro de 2017 21:04:49 PST Curious wrote:
> The way I understood it, concepts would still need a backend or be built on
> top of type traits and SFINAE, is this not correct?

And how is that different from what you proposed? You're talking about a
specific trait: an instantiation of a particular template class.

Curious

unread,
Feb 21, 2017, 8:46:34 AM2/21/17
to ISO C++ Standard - Future Proposals
I was thinking that this would be a good trait that can be put in the standard library because it can potentially have a lot of uses, maybe in implementing concepts. 

Matt Calabrese

unread,
Feb 21, 2017, 9:32:34 AM2/21/17
to ISO C++ Standard - Future Proposals
I find this useful but the cases you presented are not particularly convincing because they are better handled without SFINAE. One place this is useful is for *disabling* rather than enabling when a type is an instantiation of a particular template (this is a common pattern even in the standard library, where you have a T that shouldn't match something like in_place_type_t<T> in a constructor. A different usage is when you are dealing with a forwarding reference. For instance: template <class T, std::enable_if_t<is_instantiation_of_v<std::decay_t<T>, std::variant>, int> = 0> void foo(T&& a); In fact, the latter case is so common that you might want a version that removes reference/cv automatically.

Matt Calabrese

unread,
Feb 21, 2017, 9:40:10 AM2/21/17
to ISO C++ Standard - Future Proposals
On Tue, Feb 21, 2017 at 1:02 AM, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, February 21, 2017 at 12:26:37 AM UTC-5, Curious wrote:
Well you can't really binary search a linked list can you?  If you do try and do that, it would degrade to worse than linear time.

Right. Which is why `std::binary_search` and `std::equal_range` require random access iterators.

Nit: they actually only require forward iterators. There are several algorithms that you likely only want to call with random access iterators, but the standard allows forward iterators. Their complexity is logarithmic in terms of number of applications of the predicate, but is linear with respect to iteration.

Thiago Macieira

unread,
Feb 21, 2017, 1:30:22 PM2/21/17
to std-pr...@isocpp.org
On terça-feira, 21 de fevereiro de 2017 05:46:34 PST Curious wrote:
> I was thinking that this would be a good trait that can be put in the
> standard library because it can potentially have a lot of uses, maybe in
> implementing concepts.

I'm not saying it's a bad trait to have. But not in the use-cases you
described.

I predict that most of the cases where you would have wanted your trait would
be better served by concepts. You don't really want "any instantiation of
std::vector", you want instead a concept of vector, so that you can support
other vector types like QVector.
Reply all
Reply to author
Forward
0 new messages