Disable class specialization using concepts - c++

I'm implementing my own version of std::span using Concepts TS. I got stuck implementing these constructors:
template<class Container> constexpr span(Container& cont);
template<class Container> constexpr span(const Container& cont);
Remarks: These constructors shall not participate in overload resolution unless:
Container is not a specialization of span, and
Container is not a specialization of array
How to implement this using concepts?

First, create a trait to check for specializations. array and span look the same in the sense that they take a type parameter and a non-type parameter:
template <typename T, template <typename, auto> class Z>
struct is_specialization : std::false_type { };
template <typename A, auto V, template <typename, auto> class Z>
struct is_specialization<Z<A,V>, Z> : std::true_type { };
template <typename T, template <typename, auto> class Z>
inline constexpr bool is_specialization_v = is_specialization<T, Z>::value;
And then we can build up a concept from that:
// the last bullet point
template <typename T, typename E>
concept ValidForElement =
ConvertibleTo<std::remove_pointer_t<T>(*)[], E(*)[]>;
template <typename T, typename E>
concept AllowedContainer =
// not a specialization of span (note: requires forward declaration of span)
!is_specialization_v<std::remove_cv_t<T>, std::span>
// not a specialization of array
&& !is_specialization_v<std::remove_cv_t<T>, std::array>
// not a raw array
&& !std::is_array_v<std::remove_cv_t<T>>
&& requires (T cont) {
// data(cont) is well-formed and has a valid type
{ data(cont); } -> ValidForElement<E>
// size(cont) is well-formed
{ size(cont); }
};
Which you would use like:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <typename C> requires AllowedContainer<C, Element>
span(C&);
template <typename C> requires AllowedContainer<C const, Element>
span(C const&);
};
The const-ness requirement there prevents the nice partial-concept-id syntax, but we could just add another concept for that I guess:
template <typename T, typename E>
concept ConstAllowedContainer = AllowedContainer<T const, E>;
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C> span(C&);
template <ConstAllowedContainer<E> C> span(C const&);
};
Not sure if there's a cleverer approach here yet.
But really this whole pair-of-constructor thing is probably a mistake and you want to do a forwarding reference:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C>
span(C&&);
};
This last approach requires a few tweaks to the concept (all the remove_cv_t's should become remove_cvref_t's).

You can use type traits to check whether some type is a specialization of span or std::array. This works for me:
#include <type_traits>
template<typename, std::ptrdiff_t> class span;
template <typename T>
struct is_array : std::false_type { };
template <typename T, size_t N>
struct is_array<std::array<T, N>> : std::true_type { };
template <typename T>
struct is_span : std::false_type { };
template <typename T, std::ptrdiff_t P>
struct is_span<span<T, P>> : std::true_type { };
template <typename T>
concept bool NotSpanNotArray = !is_array<T>::value && !is_span<T>::value;
template<typename, std::ptrdiff_t> class span {
public:
template<NotSpanNotArray T> constexpr span(T& cont);
// template<NotSpanNotArray T> constexpr span(const T& cont);
};
Working demo: https://wandbox.org/permlink/M0n60U8Hl4mpacuI
Just I am not 100% sure whether such a solution meets that participate in overload resolution if and only if requirement. Some language-lawyer might clarify this.
UPDATE
I just realized that std::is_array works only for "ordinary" arrays, not std::array. Therefore I added a custom is_array type trait as well.

You are miss-using concepts. In a concept world span constructor will look like this with the concept-ts syntax:
struct span{
span(const ContiguousRange&);
span(ContiguousRange&);
span(const span&) =default;
span(span&) =default;
};
or with c++20:
struct span{
span(const ContiguousRange auto&);
span(ContiguousRange auto&);
span(const span&) =default;
span(span&) =default;
};
Concepts are here to ease abstraction. So that if your interface get more complex when using concepts then you have missed the abstraction. The abstraction here is ContiguousRange (thanks to #Lyberta).

Related

Check whether a default-deleted function template is explicitly specialized for a specific type?

(This question has been significantly edited, sorry.)
Suppose I have several non-constexpr function templates, which default to being deleted:
template <typename T> void foo() = delete;
template <typename T> int bar(int x) = delete;
// etc.
and have some explicit specialization as an exception to the general-case deletion.
I want to write code (e.g. a trait class?) which, given the identifier of one of these functions and a type T, detects, at compile-time, whether the specified function is explicitly specialized for type T. The code needs to be generic, i.e. not a separate detector for each of the functions.
Notes:
Looking for a C++11 solution.
We may assume the specified function is deleted by default - if that helps.
Ideally, it would like like instantiation_exists<decltype(foo), foo, int>::value or instantiation_exists<int>(foo, tag<int>) or instantiation_exists(foo, tag<int>) or something along those lines.
Edit: #Jarod42's wrote up an SFINAE example in a comment on an earlier version of this question, which was about a per-single-function detector. I tried to generalize/genericize it using a template-template parameter:
#include <type_traits>
template <typename T> void foo() = delete;
template <> void foo<int>() {}
template <template<typename U> typename F, typename T, typename = decltype(F<T>()) >
std::true_type test(int);
template <template<typename U> typename F, typename T>
std::false_type test(...);
template <typename T>
using foo_is_defined = decltype(test<foo<T>, T>(0));
static_assert(foo_is_defined<int>::value);
static_assert(not foo_is_defined<int*>::value);
but that was a wash (Coliru).
We cannot pass template function, or overloads in template parameter.
We can turn those function in functor:
template <typename T>
struct identity_type
{
using type = T;
};
template <typename F, typename T, typename = decltype(std::declval<F>()(identity_type<T>{})) >
std::true_type test(int);
template <typename F, typename T>
std::false_type test(...);
auto foos = [](auto tag, auto&&... args)
-> decltype(foo<typename decltype(tag)::type>((decltype(args))(args)...))
{
return foo<typename decltype(tag)::type>((decltype(args))(args)...);
};
template <typename T>
using is_foo = decltype(test<decltype(foos), T>(0));
Demo
I use generic lambda, so C++14.
in C++11, it would be really verbose:
struct foos
{
template <typename T, typename... Ts>
auto operator()(identity_type<T>, Ts&&... args) const
-> decltype(foo<T>(std::forward<Ts>(args)...))
{
return foo<T>(std::forward<Ts>(args)...);
};
};

Constructing std::hash function which accepts any iterable type

I have tried to implement a std::hash function which works on any iterator type, where the user has to implement a hashing function for their type T.
My initial implementation of the std::hash function for std::array be seen below:
template <typename T, size_t N>
struct std::hash<std::array<T, N>> {
std::size_t operator()(const std::array<T, N>& x) const {
auto hash_func = std::hash<T>{};
size_t h_val{};
for (T t : x) {
h_val = h_val ^ hash_func(t);
}
return h_val;
}
};
For supporting any iterator I have been trying to use sfinae, where my current implementation of a container type is_container can be seen below:
template <typename T, typename = void> // primary declaration
struct is_container: std::false_type {}; // when all specializations fail
template <typename T>
struct is_container< // specialization
T, // conditions:
std::void_t<decltype(std::begin(std::declval<T&>()))>
>: std::true_type {};
template <typename C> // *_v value
constexpr auto is_container_v = is_container<C>::value;
My problem is that I can't seem to match the required parameters for the struct std::hash<>.

constexpr check of a type

I'm trying to overload some function based on whether or not I'm passing an Eigen matrix to them, and I wanted to make myself some nice constexpr function to improve readability.
For that I decided to emulate the implementation of std::is_same given on https://en.cppreference.com/w/cpp/types/is_same
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
And I told myself sure, easy enough:
template <typename T>
bool constexpr is_eigen() { return false; }
template <typename T, typename Eigen::Matrix<typename T::Scalar,
T::RowsAtCompileTime,
T::ColsAtCompileTime,
T::Options,
T::MaxRowsAtCompileTime,
T::MaxColsAtCompileTime>>
bool constexpr is_eigen() { return true; }
However my Eigen types resolve to the first template specialization, not the first (putting a dummy typename U doesn't help).
I also tried something like:
template <typename T, bool is_it = std::is_same<T,
Eigen::Matrix<typename T::Scalar,
T::RowsAtCompileTime,
T::ColsAtCompileTime,
T::Options,
T::MaxRowsAtCompileTime,
T::MaxColsAtCompileTime>>::value>
bool constexpr is_eigen() { return is_it; }
template <typename T, typename = std::enable_if_t<!std::is_class<T>::value>>
bool constexpr is_eigen() { return false; }
But for non-Eigen classes the first overload doesn't resolve, and trying anything to change that means Eigen will still hit the false branch
Basically, any default branch I come up with gets taken even for Eigen-types. I hate SFINAE :(
You can use partial specialization to match a Eigen::Matrix<...> like so
template <typename T>
struct is_eigen_impl : std::false_type {};
template <typename T, int... Is>
struct is_eigen_impl<Eigen::Matrix<T, Is...>> : std::true_type {};
template <typename T>
constexpr bool is_eigen = is_eigen_impl<T>::value;
If I understand correctly, you trying to obtain something as follows (caution: code not tested)
template <typename T>
constexpr std::false_type is_eigen_helper (T const &);
template <typename T, int ... Is>
constexpr std::true_type is_eigen_helper (Eigen::Matrix<T, Is...> const &);
template <typename T>
constexpr auto is_eigen { decltype(is_eigen_helper(std::declval<T>()))::value };
In this case is_eigen<T> is a template variable, so is required C++14.
In C++11 you can define is_eigen<T> as a type
template <typename T>
using is_eigen = decltype(is_eigen_helper(std::declval<T>()));
so you can use is_eigen<T>::value to check if T is an Eigen::Matrix.
p.s.: template specialization, as in super's answer, is another (maybe better) way to do almost the same thing.
But, as pointed by Jarod42, there is a difference.
With my solution you obtain that is_eigen<T> (or is_eigen<T>::value, in C++11) is true when T is a Eigen::Matrix of some type or a class that inherits from some Eigen::Matrix class.
With super's solution you get that is_eigen<T>::value is true only if T is an Eigen::Matrix. When T inherit from an Eigen::Matrix, is_eigen<T> is false.
See you what is better for your needs.

restrict a template function, to only allow certain types

Here say I have a simple template function that in principle can accept all kind of types:
template <class Type>
std::ostream& operator<< (std::ostream& stream, const Type subject) {
stream << "whatever, derived from subject\n";
return stream; }
I only want to use this template to cout a few types, say std::vector and boost::array objects. However whenever I use cout to other types even elementary types, e.g. std::cout << int(5);, will be a compilation error, because there are two possible implementations of operator<<(std::ostream, int) now, one is in standard c++, the other specified by my template function.
I would like to ask, is it possible to restrict my template function, so that it only accepts a few types specified by me? That is how to tell the compiler to ignore my template when i use cout << int(5). Thanks in advance.
To be more clear, this is what I want to do:
template <class Type>
std::ostream& operator<< (std::ostream& stream, const Type subject) {
if (Type == TypeA or TypeB or TypeC) //use this template and do these {...};
else //ignore this template, and use operator<< provided in standard c++ library.
}
Writing a really generic solution for this is hard. The problem with checking an arbitrary type T against std::vector or std::array is that the latter are not classes, they are class templates. Even worse, std::array is a class template with a non-type template parameter, so you can't even have a parameter pack which will hold both std::vector and std::array.
You can get around this somewhat by explicitly wrapping non-type parameters up in types, but it gets ugly, fast.
Here is a solution I came up with that will support any class or template class with no non-type template parameters by default. Template classes with non-type template parameters can be supported by adding a wrapper type to map non-type parameters to type parameters.
namespace detail{
//checks if two types are instantiations of the same class template
template<typename T, typename U> struct same_template_as: std::false_type {};
template<template<typename...> class X, typename... Y, typename... Z>
struct same_template_as<X<Y...>, X<Z...>> : std::true_type {};
//this will be used to wrap template classes with non-type args
template <typename T>
struct wrapImpl { using type = T; };
//a wrapper for std::array
template <typename T, typename N> struct ArrayWrapper;
template <typename T, std::size_t N>
struct ArrayWrapper<T, std::integral_constant<std::size_t, N>> {
using type = std::array<T,N>;
};
//maps std::array to the ArrayWrapper
template <typename T, std::size_t N>
struct wrapImpl<std::array<T,N>> {
using type = ArrayWrapper<T,std::integral_constant<std::size_t,N>>;
};
template <typename T>
using wrap = typename wrapImpl<typename std::decay<T>::type>::type;
//checks if a type is the same is one of the types in TList,
//or is an instantiation of the same template as a type in TempTList
//default case for when this is false
template <typename T, typename TList, typename TempTList>
struct one_of {
using type = std::false_type;
};
//still types in the first list to check, but the first one doesn't match
template <typename T, typename First, typename... Ts, typename TempTList>
struct one_of<T, std::tuple<First, Ts...>, TempTList> {
using type = typename one_of<T, std::tuple<Ts...>, TempTList>::type;
};
//type matches one in first list, return true
template <typename T, typename... Ts, typename TempTList>
struct one_of<T, std::tuple<T, Ts...>, TempTList> {
using type = std::true_type;
};
//first list finished, check second list
template <typename T, typename FirstTemp, typename... TempTs>
struct one_of<T, std::tuple<>, std::tuple<FirstTemp, TempTs...>> {
//check if T is an instantiation of the same template as first in the list
using type =
typename std::conditional<same_template_as<wrap<FirstTemp>, T>::value,
std::true_type,
typename one_of<T, std::tuple<>, std::tuple<TempTs...>>::type>::type;
};
}
//top level usage
template <typename T, typename... Ts>
using one_of = typename detail::one_of<detail::wrap<T>,Ts...>::type;
struct Foo{};
struct Bar{};
template <class Type>
auto operator<< (std::ostream& stream, const Type subject)
//is Type one of Foo or Bar, or an instantiation of std::vector or std::array
-> typename std::enable_if<
  one_of<Type, std::tuple<Foo,Bar>, std::tuple<std::vector<int>,std::array<int,0>>
>::value, std::ostream&>::type
{
stream << "whatever, derived from subject\n";
return stream;
}
Please don't use this, it's horrible.
Live Demo
You can restrict your overload like this:
template <class T>
std::ostream& my_private_ostream( std::ostream& stream, const T& data )
{ <your implementation> }
template <class T, class A>
std::ostream& operator<< ( std::ostream& stream, const std::vector<T,A>& data )
{ return my_private_ostream(stream,data); }
Same for std::arrays (you should tag your question with c++11):
template <class T, size_t N>
std::ostream& operator<< ( std::ostream& stream, const std::array<T,N>& data )
{ return my_private_ostream(stream,data); }
Alternatively, for a solution that looks a bit more like your edit, you could use C++11 enable_if, although I have a personal aversion to them as they tend to make the code difficult to read and maintain. So I strongly recommend the previous solution.
// Vector type predicate
template <class T>
struct is_vector: std::false_type {};
template <class T, class A>
struct is_vector< std::vector<T,A> >: std::true_type {};
// Array type predicate
template <class T>
struct is_array: std::false_type {};
template <class T, size_t N>
struct is_array< std::array<T,N> >: std::true_type {};
// The overload with the syntax you want
template <class Indexable>
typename std::enable_if<
is_vector<Indexable>::value || is_array<Indexable>::value,
std::ostream&
>::type
operator<< ( std::ostream& stream, const Indexable& data )
{ <your implementation> }
Use SFINAE to do what you're asking.
template<typename...>
struct is_vector: std::false_type{};
template<typename T, typename Alloc>
struct is_vector<std::vector<T, Alloc>>: std::true_type{};
template<typename...>
struct is_array: std::false_type{};
template<typename T, std::size_t Size>
struct is_array<std::array<T, Size>>: std::true_type{};
template<typename T>
struct is_my_ostream_type{
enum {
value = is_vector<T>::value || is_array<T>::value
};
};
template<
typename T,
typename = typename std::enable_if<is_my_ostream_type<T>::value>::type
>
std::ostream &operator <<(std::ostream &lhs, const T &rhs){
lhs << "is my special ostream overload";
return lhs;
}
But you're probably going to end up just writing an overload for every type rather than doing this.

Is T an instance of a template in C++?

Suppose I'm in a template and I want to know if a type parameter T is an instantiation of a particular template, e.g., std::shared_ptr:
template<typename T>
void f(T&& param)
{
if (instantiation_of(T, std::shared_ptr)) ... // if T is an instantiation of
// std::shared_ptr...
...
}
More likely I'd want to do this kind of test as part of a std::enable_if test:
template<typename T>
std::enable_if<instantiation_of<T, std::shared_ptr>::type
f(T&& param)
{
...
}
// other overloads of f for when T is not an instantiation of std::shared_ptr
Is there a way to do this? Note that the solution needs to work with all possible types and templates, including those in the standard library and in other libraries I cannot modify. My use of std::shared_ptr above is just an example of what I might want to do.
If this is possible, how would I write the test myself, i.e., implement instantiation_of?
Why use enable_if when simple overloading suffices?
template<typename T>
void f(std::shared_ptr<T> param)
{
// ...
}
If you really do need such a trait, I think this should get you started (only roughly tested with VC++ 2010):
#include <type_traits>
template<typename>
struct template_arg;
template<template<typename> class T, typename U>
struct template_arg<T<U>>
{
typedef U type;
};
template<typename T>
struct is_template
{
static T* make();
template<typename U>
static std::true_type check(U*, typename template_arg<U>::type* = nullptr);
static std::false_type check(...);
static bool const value =
std::is_same<std::true_type, decltype(check(make()))>::value;
};
template<
typename T,
template<typename> class,
bool Enable = is_template<T>::value
>
struct specialization_of : std::false_type
{ };
template<typename T, template<typename> class U>
struct specialization_of<T, U, true> :
std::is_same<T, U<typename template_arg<T>::type>>
{ };
A partial spec should be able to do it.
template <template <typename...> class X, typename T>
struct instantiation_of : std::false_type {};
template <template <typename...> class X, typename... Y>
struct instantiation_of<X, X<Y...>> : std::true_type {};
http://ideone.com/4n346
I actually had to look up the template template syntax, because I've basically never had cause to use it before.
Not sure how this interacts with templates like std::vector with additional defaulted arguments.
Best way to do it when dealing with a T&& is to make sure you remove_reference before doing the check, because the underlying type T can be a reference or a value type, and template partial specialization has to be exact to work. Combined with an answer above the code to do it could be:
template <
typename T,
template <typename...> class Templated
> struct has_template_type_impl : std::false_type {};
template <
template <typename...> class T,
typename... Ts
> struct has_template_type_impl<T<Ts...>, T> : std::true_type {};
template <
typename T,
template <typename...> class Templated
> using has_template_type = has_template_type_impl<
typename std::remove_reference<T>::type,
Templated
>;
And then you just enable_if your way to victory:
template <typename T>
typename std::enable_if<has_template_type<T, std::shared_ptr>::value>::type
f(T&& param)
{
// ...
}