I want to write a templatized function which takes either an array<int, 3> or an int[3]. I'm trying to capture that in an enable_if:
template<typename T>
enable_if_t<is_array_v<T> && extent_v<T> == 3U || !is_array_v<T> && tuple_size<T>::value == 3U> foo(const T& param) {}
Unfortunately for an int[3], tupple_size is not defined, which causes the template to fail to compile, before short circuiting is evaluated.
I have also tried to do this using a conditional but that has the same problem of ensuring both options are valid for T before considering the condition.
I know that I can do this by specializing. But the code is the exact same in the body of the function. I hate the fact that I'm specializing when the implementation is the same.
Is there a way I can force the short circuit before evaluating the conditions?
Taking advantage of the fact that extent<T> for non-array types is zero and hence falsy, and disjunction derives from the first truthy type in the list with short circuiting:
template<typename T>
enable_if_t<disjunction<extent<T>, tuple_size<T>>::value == 3U> foo(const T& param) {}
This is probably too clever. Note that you can't use disjunction_v here.
conditional should work just fine too. The trick is to not ask for ::value until you've picked the right type:
template<typename T>
enable_if_t<conditional_t<is_array_v<T>, extent<T>, tuple_size<T>>::value == 3U>
foo(const T& param) {}
In short no, the template substitutions always have to be valid. It would probably be easier to just define a specific template to match the arrays:
template <typename T>
struct IsArrayInt3 { enum: bool { value = false }; };
template <>
struct IsArrayInt3<int[3]> { enum: bool { value = true }; };
template <>
struct IsArrayInt3<std::array<int, 3>> { enum: bool { value = true }; };
I would suggest a alternate approach: 2 overloads (always prefer overloads to template specializations) that call a common function which contains the common code:
namespace detail
{
template <class T>
auto foo_impl(const T& a)
{
// common code
}
}
template <class T>
auto foo(const std::array<T, 3>& a)
{
detail::foo_impl(a);
}
template <class T>
auto foo(const T(&a)[3])
{
detail::foo_impl(a);
}
This is clear, hassle-free and avoids code repetition.
An alternate is to create your own trait:
template <class T, std::size_t Size>
struct my_is_array : std::false_type
{};
template <class T, std::size_t Size>
struct my_is_array<std::array<T, Size>, Size> : std::true_type
{};
template <class T, std::size_t Size>
struct my_is_array<T[Size], Size> : std::true_type
{};
template<typename T>
std::enable_if_t<my_is_array<T, 3>::value> foo(const T& param) {}
or (I actually like this one better):
template <class T>
struct array_size_or_zero : std::integral_constant<std::size_t, 0>
{};
template <class T, std::size_t Size>
struct array_size_or_zero<std::array<T, Size>> : std::integral_constant<std::size_t, Size>
{};
template <class T, std::size_t Size>
struct array_size_or_zero<T[Size]> : std::integral_constant<std::size_t, Size>
{};
template<typename T>
std::enable_if_t<array_size_or_zero<T>::value == 3> foo(const T& param) {}
Careful!!: foo must have parameter by reference, otherwise the array decays to pointer.
Related
I'm trying to write syntactic sugar, in a monad-style, over std::optional. Please consider:
template<class T>
void f(std::optional<T>)
{}
As is, this function cannot be called with a non-optional T1 (e.g. an int), even though there exists a conversion from T to std::optional<T>2.
Is there a way to make f accept an std::optional<T> or a T (converted to an optional at the caller site), without defining an overload3?
1) f(0): error: no matching function for call to 'f(int)' and note: template argument deduction/substitution failed, (demo).
2) Because template argument deduction doesn't consider conversions.
3) Overloading is an acceptable solution for a unary function, but starts to be an annoyance when you have binary functions like operator+(optional, optional), and is a pain for ternary, 4-ary, etc. functions.
Another version. This one doesn't involve anything:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class template argument deduction already does the right thing here. If t is an optional, the copy deduction candidate will be preferred and we get the same type back. Otherwise, we wrap it.
Instead of taking optional as argument take deductible template parameter:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
Live example
This uses one of my favorite type traits, which can check any all-type template against a type to see if it's the template for it.
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
https://godbolt.org/z/HXgoEE
Another version. This one doesn't involve writing traits:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}
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<>.
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).
In https://stackoverflow.com/a/1967183/134841, a solution is provided for statically checking whether a member exists, possibly in a subclass of a type:
template <typename Type>
class has_resize_method
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin
{
void resize(int){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};
However, it doesn't work on C++11 final classes, because it inherits from the class under test, which final prevents.
OTOH, this one:
template <typename C>
struct has_reserve_method {
private:
struct No {};
struct Yes { No no[2]; };
template <typename T, typename I, void(T::*)(I) > struct sfinae {};
template <typename T> static No check( ... );
template <typename T> static Yes check( sfinae<T,int, &T::reserve> * );
template <typename T> static Yes check( sfinae<T,size_t,&T::reserve> * );
public:
static const bool value = sizeof( check<C>(0) ) == sizeof( Yes ) ;
};
fails to find the reserve(int/size_t) method in baseclasses.
Is there an implementation of this metafunction that both finds reserved() in baseclasses of T and still works if T is final?
Actually, things got much easier in C++11 thanks to the decltype and late return bindings machinery.
Now, it's just simpler to use methods to test this:
// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
return true;
}
// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }
You can then use this in a class for example:
template <typename T, bool b>
struct Reserver {
static void apply(T& t, size_t n) { t.reserve(n); }
};
template <typename T>
struct Reserver <T, false> {
static void apply(T& t, size_t n) {}
};
And you use it so:
template <typename T>
bool reserve(T& t, size_t n) {
Reserver<T, has_reserve_method(t)>::apply(t, n);
return has_reserve_method(t);
}
Or you can choose a enable_if method:
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
t.reserve(n);
return true;
}
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
return false;
}
Note that this switching things is actually not so easy. In general, it's much easier when just SFINAE exist -- and you just want to enable_if one method and not provide any fallback:
template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
t.reserve(n);
}
If substitution fails, this method is removed from the list of possible overloads.
Note: thanks to the semantics of , (the comma operator) you can chain multiple expressions in decltype and only the last actually decides the type. Handy to check multiple operations.
A version that also relies on decltype but not on passing arbitrary types to (...) [ which is in fact a non-issue anyway, see Johannes' comment ]:
template<typename> struct Void { typedef void type; };
template<typename T, typename Sfinae = void>
struct has_reserve: std::false_type {};
template<typename T>
struct has_reserve<
T
, typename Void<
decltype( std::declval<T&>().reserve(0) )
>::type
>: std::true_type {};
I'd like to point out according to this trait a type such as std::vector<int>& does support reserve: here expressions are inspected, not types. The question that this trait answers is "Given an lvalue lval for such a type T, is the expressions lval.reserve(0); well formed". Not identical to the question "Does this type or any of its base types has a reserve member declared".
On the other hand, arguably that's a feature! Remember that the new C++11 trait are of the style is_default_constructible, not has_default_constructor. The distinction is subtle but has merits. (Finding a better fitting name in the style of is_*ible left as an exercise.)
In any case you can still use a trait such as std::is_class to possibly achieve what you want.
I'm trying to create a set of overloaded templates for arrays/pointers where one template will be used when the compiler knows the size of the array and the other template will be used when it doesn't:
template <typename T, size_t SZ>
void moo(T (&arr)[SZ])
{ ... }
template <typename T>
void moo(T *ptr)
{ ... }
The problem is that when the compiler knows the size of the array, the overloads are ambiguous and the compile fails.
Is there some way to resolve the ambiguity (perhaps via SFINAE) or is this just not possible.
It is possible as it can be determined wether a template parameter is an array or not:
template<class T> struct is_array {
enum { value = false };
};
template<class T, size_t N> struct is_array<T[N]> {
enum { value = true };
};
template<class T> void f(T const&) {
std::cout << is_array<T>::value << std::endl;
}
Combining that with enable_if, the above can be made unambiguous. For example using Boost.TypeTraits:
template <typename T, size_t SZ>
typename boost::enable_if<boost::is_array<T>, void>::type
f(T (&arr)[SZ]) {}
With references however there is no need for SFINAE at all:
template<class T, size_t SZ> void f(T (&arr)[SZ]) {}
template<class T> void f(T* const& t) {}
Johannes brings up another option that fits the situation at hand better - using SFINAE for the problematic pointer overload instead:
template <typename T, size_t SZ> void f(T (&arr)[SZ]) {}
template <typename T>
typename boost::enable_if<boost::is_pointer<T>, void>::type
f(T ptr) {}
And probably the easiest solution:
template <typename T, size_t SZ>
void moo(T (&arr)[SZ])
{ ... }
template <typename T>
inline void moo(T ptr) { __moo(ptr); }
template <typename T>
void __moo(T* ptr)
{ ... }
You can call the function explicitly:
int a[1] = {0}
moo<int,1>(a);
Or you can overload on const:
template<class T>
void moo(T const* p) { }
// ...
moo(a); // not const, array
int* p = 0;
moo(p); // pointer