STL: 'all_of', 'none_of' and 'any_of' for type traits

335 views
Skip to first unread message

c....@gmx.de

unread,
Aug 12, 2016, 2:11:40 AM8/12/16
to ISO C++ Standard - Future Proposals
I'm not following the standardization process that closely and although I did some quick research I'm not sure whether something like this already has been proposed or is simply to trivial to be considered for the standard.

When working with template-heavy code it is often necessary to use type traits, especially when using SFINAE to for differentiate function calls. With many arguments these conditions sometimes get very large, can be hard to read and bloat the code.

Imagine you have several matrix types and want to write a function that adds an arbitrary combination of them together. It could be useful to differentiate between the matrices and other types (e.g. vectors) that might also have an add function. This could be done via a type trait 'is_matrix'.

A classic version could look like the following:

template <typename TMatrix1, typename TMatrix2, typename TMatrix3, typename TMatrix4, typename TMatrix5>
auto add(const TMatrix1& m1, const TMatrix2& m2, const TMatrix1& m3, const TMatrix2& m4, TMatrix3& r)
 
-> std::enable_if_t<is_matrix<TMatrix1>::value && is_matrix<TMatrix2>::value && is_matrix<TMatrix3>::value &&
 is_matrix
<TMatrix4>::value && is_matrix<TMatrix5>::value>;

  
Now this of course could be simplified by using variable templates -- which might not always be available:

template <typename TMatrix1, typename TMatrix2, typename TMatrix3, typename TMatrix4, typename TMatrix5>
auto add(const TMatrix1& m1, const TMatrix2& m2, const TMatrix1& m3, const TMatrix2& m4, TMatrix3& r)
 
->std::enable_if_t<is_matrix_v<TMatrix1> && is_matrix_v<TMatrix2> && is_matrix_v<TMatrix3> &&
 is_matrix_v
<TMatrix4> && is_matrix_v<TMatrix5>>;

  
Or alternatively in C++17:

template <typename TMatrix1, typename TMatrix2, typename TMatrix3, typename TMatrix4, typename TMatrix5>
auto add(const TMatrix1& m1, const TMatrix2& m2, const TMatrix1& m3, const TMatrix2& m4, TMatrix3& r)
-> std::enable_if_t<std::conjunction_v<is_matrix<TMatrix1>, is_matrix<TMatrix2>,
 is_matrix
<TMatrix3>, is_matrix<TMatrix4>, is_matrix<TMatrix5>>;

  
This problem gets even worse with longer names for the type traits:

template <typename T1, typename T2, typename T3, typename T4>
auto foo(T1&& t1, T2&& t2, T3&& t3, T4&& t4)
{
 
using namespace std;
 
static_assert(has_unique_object_representations_v<T1> && has_unique_object_representations_v<T2> &&
 has_unique_object_representations_v
<T3> && has_unique_object_representations_v<T4>, "not allowed");
}


What about variadic templates where you only have a parameter pack? In C++17 this can be done with a little trickery, if you do know how:

template <typename... Ts>
auto bar(Ts&&... ts)
{
 
using namespace std;
 
static_assert(conjunction_v<negation<is_lvalue_reference<Ts>>...>, "not allowed");
}


As you can see, although many of this is already currently possible -- or probably will be in C++17 -- the solutions are longer than you want them to and/or require some tricks. Of course these problems can be mitigated by using variable and alias templates or implementing custom types. But that is all extra code that you have to implement.

Why not have 'all_of', 'none_of' or 'any_of' meta types in the STL, similar to those in the algorithm header, only for type traits?

Above examples could be written like this:

template <typename TMatrix1, typename TMatrix2, typename TMatrix3, typename TMatrix4, typename TMatrix5>
auto add(const TMatrix1& m1, const TMatrix2& m2, const TMatrix1& m3, const TMatrix2& m4, TMatrix3& r)
->std::enable_if_t<std::all_of_v<is_matrix, TMatrix1, TMatrix2, TMatrix3, TMatrix4, TMatrix5>>;


template <typename T1, typename T2, typename T3, typename T4>
auto foo(T1&& t1, T2&& t2, T3&& t3, T4&& t4)
{
 
using namespace std;
 
static_assert(all_of_v<has_unique_object_representations, T1, T2, T3, T4>, "not allowed");
}


template <typename... Ts>
auto bar(Ts&&... ts)
{
 
using namespace std;
 
static_assert(none_of_v<is_lvalue_reference, Ts...>, "not allowed");
}


Not only are these solutions shorter, they also make the intention clearer and the code probably easier to understand. Template-heavy code ist hard to read as it is and does not need to get bloated even further when unnecessary. This is a tiny proposal that can be implemented in a quite elegant way using already proposed C++17 features:

template <template <typename> class Pred, typename... Ts>
struct all_of : std::conjunction<Pred<Ts>...>::type {};


template <template <typename> class Pred, typename... Ts>
constexpr bool all_of_v = all_of<Pred, Ts...>::value;


template <template <typename> class Pred, typename... Ts>
struct none_of : std::conjunction<std::negation<Pred<Ts>>...>::type {};


template <template <typename> class Pred, typename... Ts>
constexpr bool none_of_v = none_of<Pred, Ts...>::value;


template <template <typename> class Pred, typename... Ts>
struct any_of : std::disjunction<Pred<Ts>...>::type {};


template <template <typename> class Pred, typename... Ts>
constexpr bool any_of_v = any_of<Pred, Ts...>::value;


This of course would also allow an easy implementation of 'enable_if_all', 'enable_if_none' and 'enable_if_any'.

This should conform to the standard, but I'm no one hundred percent sure. At the moment I can only test it with VS2015.

I'm aware some of the examples might be a little contrived, but I hope you get my point :)

Thoughts?     

Ville Voutilainen

unread,
Aug 12, 2016, 5:06:05 AM8/12/16
to ISO C++ Standard - Future Proposals
On 12 August 2016 at 09:11, <c....@gmx.de> wrote:
> This should conform to the standard, but I'm no one hundred percent sure. At
> the moment I can only test it with VS2015.

Tip: use an online compiler, like the one at melpon.org/wandbox.

> I'm aware some of the examples might be a little contrived, but I hope you
> get my point :)


all_of, any_of and none_of are existing algorithms, so having them as
traits will necessitate
different names for the traits.

Other than that, I would certainly like shorter names for conjunction,
disjunction and negation.
The question is, would it be simply better to have those shorter names
to begin with, rather
than introduce wrappers that merely shorten the names. :)

c....@gmx.de

unread,
Aug 12, 2016, 10:45:17 AM8/12/16
to ISO C++ Standard - Future Proposals
Thank you for your feedback. I think there may have been a misunderstanding. The proposed meta traits are not merely shorter names or wrappers for 'std::conjunction' or 'std::disjunction'. Since those already have been voted into the standard (as far as i know), proposing something identical with a different name would be moot.

But there is an important difference. The already existing 'std::conjunction' and 'std::disjunction' both take instantiated type traits and combine the (short-circuited) results. The proposed meta traits however, take an single uninstantiated type trait and a list of types, which then gets applied to all types.

They are not the same but rather would have a nice synergy with one another. You could for example write something like this:

template <typename... Ts>
auto foo(Ts&&...)
-> std::enable_if_t<std::conjunction_v<
all_of<std::is_rvalue_reference, Ts&&...>,
none_of<std::is_pointer, Ts...>,
none_of<std::is_integral, Ts...>>>;

Regarding the names I'm not sure if this is an issue or not, since the C++ standard, as far as I know, allows functions and types that have the same name. However, the names can be changed to something else when needed.

And thank you for the link to wandbox which provides the newest GCC version. I tried IDEOne, but their version of GCC does not support the necessary C++17 features yet. I can confirm that the proposal also works on the GCC ´version provided by wandbox.

Ville Voutilainen

unread,
Aug 12, 2016, 11:08:00 AM8/12/16
to ISO C++ Standard - Future Proposals
On 12 August 2016 at 17:45, <c....@gmx.de> wrote:
> Thank you for your feedback. I think there may have been a misunderstanding.
> The proposed meta traits are not merely shorter names or wrappers for
> 'std::conjunction' or 'std::disjunction'. Since those already have been
> voted into the standard (as far as i know), proposing something identical
> with a different name would be moot.
>
> But there is an important difference. The already existing
> 'std::conjunction' and 'std::disjunction' both take instantiated type traits
> and combine the (short-circuited) results. The proposed meta traits however,
> take an single uninstantiated type trait and a list of types, which then
> gets applied to all types.
>
> They are not the same but rather would have a nice synergy with one another.
> You could for example write something like this:
>
> template <typename... Ts>
> auto foo(Ts&&...)
> -> std::enable_if_t<std::conjunction_v<
> all_of<std::is_rvalue_reference, Ts&&...>,
> none_of<std::is_pointer, Ts...>,
> none_of<std::is_integral, Ts...>>>;

Ok. Why would I do that, instead of using a fold-expression? I can already write

(is_rvalue_reference_v<Ts&&> && ...)

instead of the proposed

all_of<is_rvalue_reference, Ts&&...>

and if I want to turn the expression's result back into a trait-type,
I can just wrap it
back into a bool_constant.

Note: I have an answer to that question. It's more or less the same as
the answer for logical traits,
as explained in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0013r0.html
which is that the traits can short-circuit better, and also the traits
can easily be provided for
pre-C++17 compilers.

> Regarding the names I'm not sure if this is an issue or not, since the C++
> standard, as far as I know, allows functions and types that have the same
> name. However, the names can be changed to something else when needed.

You can't have a function template and a class template with the same
name, regardless
of other kinds of different entities being able to use the same name,
like non-template
functions and structs, or types and variables.

Bo Persson

unread,
Aug 12, 2016, 11:09:19 AM8/12/16
to std-pr...@isocpp.org
On 2016-08-12 16:45, c....@gmx.de wrote:
>
>
> On Friday, August 12, 2016 at 11:06:05 AM UTC+2, Ville Voutilainen wrote:
>
> On 12 August 2016 at 09:11, <c....@gmx.de <javascript:>> wrote:
> > This should conform to the standard, but I'm no one hundred
> percent sure. At
> > the moment I can only test it with VS2015.
>
> Tip: use an online compiler, like the one at melpon.org/wandbox
> <http://melpon.org/wandbox>.
>
> > I'm aware some of the examples might be a little contrived, but I
> hope you
> > get my point :)
>
>
> all_of, any_of and none_of are existing algorithms, so having them as
> traits will necessitate
> different names for the traits.
>
> Other than that, I would certainly like shorter names for conjunction,
> disjunction and negation.
> The question is, would it be simply better to have those shorter names
> to begin with, rather
> than introduce wrappers that merely shorten the names. :)
>
>
> Thank you for your feedback. I think there may have been a
> misunderstanding. The proposed meta traits are not merely shorter names
> or wrappers for 'std::conjunction' or 'std::disjunction'. Since those
> already have been voted into the standard (as far as i know), proposing
> something identical with a different name would be moot.
>


The orginal proposal was for naming the traits and_ and or_, but those
were rejected as being too close to other names.

The uglier names std::conjunction and std::disjunction were chosen
specifically to not interfere with others. I don't think a proposal to
rename them back will be accepted. :-)

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0013r1.html



Bo Persson



Ville Voutilainen

unread,
Aug 12, 2016, 11:11:59 AM8/12/16
to ISO C++ Standard - Future Proposals
On 12 August 2016 at 18:09, Bo Persson <b...@gmb.dk> wrote:
> The orginal proposal was for naming the traits and_ and or_, but those were
> rejected as being too close to other names.
>
> The uglier names std::conjunction and std::disjunction were chosen
> specifically to not interfere with others. I don't think a proposal to
> rename them back will be accepted. :-)
>
> http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0013r1.html


I can easily imagine such a proposal not even being considered, but
there are other mechanisms
to send messages to the committee besides proposals, and those
mechanisms mandate consideration
and an answer. However, that's beside the point, because the names of
the logical traits are indeed
not really the topic here.

c....@gmx.de

unread,
Aug 12, 2016, 11:35:56 AM8/12/16
to ISO C++ Standard - Future Proposals


On Friday, August 12, 2016 at 5:08:00 PM UTC+2, Ville Voutilainen wrote:

You can't have a function template and a class template with the same
name, regardless
of other kinds of different entities being able to use the same name,
like non-template
functions and structs, or types and variables.

You are right, I just tested this. Thanks for the correction. However, I chose the names primarily to show the motivation behind the idea. And as I said, names can be changed if necessary.

3dw...@verizon.net

unread,
Aug 24, 2016, 2:09:33 PM8/24/16
to ISO C++ Standard - Future Proposals, c....@gmx.de

Won't fold expressions allow something like this?

template<typename... TMatrixen>
 
auto add(const TMatrixen&... m)
 
-> std::enable_if_t<(... && is_matrix_v<TMatrixen>), matrix<double>>;


my syntax may be off but I think something like it should do the trick.
OTOH, just because something is easier to implement in the language doesn't make the library components inutil - they are nicely readable.

c....@gmx.de

unread,
Aug 24, 2016, 2:36:56 PM8/24/16
to ISO C++ Standard - Future Proposals, c....@gmx.de, 3dw...@verizon.net
Yes, fold expressions would allow something like this, but that would only work if you have a parameter pack. Some of the examples I showed in the initial post illustrate how you could also utilize the proposed meta traits with a fixed number of template parameters.

Beside the readability, there would also be other benefits. Since these could be implemented on the basis of 'std::conditional' etc. (see initial post), you would get all the advantages from those, such as short-circuiting (see the second post from Ville)

Farid Mehrabi

unread,
Aug 25, 2016, 2:31:49 PM8/25/16
to std-proposals
Isn't the 'concept' proposal what you might possibly look for? It is a long on-going discussion. you can just google for 'c++ concept'. 

regards,
FM.

--
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/86d96e28-f584-4c32-86dc-41f9906c7bda%40isocpp.org.



--
how am I supposed to end the twisted road of  your hair in such a dark night??
unless the candle of your face does shed some light upon my way!!!
Reply all
Reply to author
Forward
0 new messages