In template meta programming, one can use SFINAE on the return type to choose a certain template member function, i.e.
template<int N> struct A {
int sum() const noexcept
{ return _sum<N-1>(); }
private:
int _data[N];
template<int I> typename std::enable_if< I,int>::type _sum() const noexcept
{ return _sum<I-1>() + _data[I]; }
template<int I> typename std::enable_if<!I,int>::type _sum() const noexcept
{ return _data[I]; }
};
However, this doesn't work on constructors. Suppose, I want to declare the constructor
template<int N> struct A {
/* ... */
template<int otherN>
explicit(A<otherN> const&); // only sensible if otherN >= N
};
but disallow it for otherN < N.
So, can SFINAE be used here? I'm only interested in solutions which allow automatic template-parameter deduction, so that
A<4> a4{};
A<5> a5{};
A<6> a6{a4}; // doesn't compile
A<3> a3{a5}; // compiles and automatically finds the correct constructor
Note: this is a very simplified example where SFINAE may be overkill and static_assert may suffice. However, I want to know whether I can use SFINAE instead.
You can add a defaulted type argument to the template:
template <int otherN, typename = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
There are many ways to trigger SFINAE, being enable_if just one of them.
First of all:
What is std::enable_if ?
It's just this:
template<bool, class T=void> enable_if{ typedef T type; };
template<class T> enable_if<false,T> {};
template<bool b, class T=void> using enable_if_t = typename enable_f<b,T>::type;
The idea is to make typename enable_if<false>::type to be an error, hence make any template declaration containing it skipped.
So how can this trigger function selection?
Disabling functions
The idea is making the declaration erroneous in some part:
By return type
template<class Type>
std::enable_if_t<cond<Type>::value,Return_type> function(Type);
By a actual parameter
template<class Type>
return_type function(Type param, std::enable_if_t<cond<Type>::value,int> =0)
By a template parameter
template<class Type,
std::enable_if_t<cond<Type>::value,int> =0> //note the space between > and =
return_type function(Type param)
Selecting functions
You can parametrise different alternatives with tricks like this:
tempplate<int N> struct ord: ord<N-1>{};
struct ord<0> {};
template<class T, std::enable_if<condition3, int> =0>
retval func(ord<3>, T param) { ... }
template<class T, std::enable_if<condition2, int> =0>
retval func(ord<2>, T param) { ... }
template<class T, std::enable_if<condition1, int> =0>
retval func(ord<1>, T param) { ... }
template<class T> // default one
retval func(ord<0>, T param) { ... }
// THIS WILL BE THE FUCNTION YOU'LL CALL
template<class T>
retval func(T param) { return func(ord<9>{},param); } //any "more than 3 value"
This will call the first/second/third/fourth function if condition3 is satisfied, than condition2 than condition1 than none of them.
Other SFINAE triggers
Writing compile-time conditions can be either a matter of explicit specialization or a matter of unevaluated expression success/failure:
for example:
template<class T, class = void>
struct is_vector: std::false_type {};
template<class X>
struct is_vector<vector<X> >:: std::true_type {};
so that is_vector<int>::value is false but is_vecttor<vector<int> >::value is true
Or, by means of introspection, like
template<class T>
struct is_container<class T, class = void>: std::false_type {};
template<class T>
struct is_container<T, decltype(
std::begin(std::declval<T>()),
std::end(std::declval<T>()),
std::size(std::declval<T>()),
void(0))>: std::true_type {};
so that is_container<X>::value will be true if given X x, you can compile std::begin(x) etc.
The trick is that the decltype(...) is actually void (the , operator discards the previous expressions) only if all the sub-expressions are compilable.
There can be even many other alternatives. Hope between all this you can find something useful.
The accepted answer is good for most cases, but fails if two such constructor overloads with different conditions are present. I'm looking for a solution in that case too.
Yes: the accepted solution works but not for two alternative constructor as, by example,
template <int otherN, typename = typename std::enable_if<otherN == 1>::type>
explicit A(A<otherN> const &);
template <int otherN, typename = typename std::enable_if<otherN != 1>::type>
explicit A(A<otherN> const &);
because, as stated in this page,
A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.
As proposed in the same page, you can go around this problem applying SFINAE, modifying the signature, to the type of a value (not type) template parameter as follows
template <int otherN, typename std::enable_if<otherN == 1, bool>::type = true>
explicit A(A<otherN> const &);
template <int otherN, typename std::enable_if<otherN != 1, bool>::type = true>
explicit A(A<otherN> const &);
In C++11, you can use a defaulted template parameter:
template <int otherN, class = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
However, if your compiler doesn't support defaulted template parameters yet, or you need multiple overloads, then you can use a defaulted function parameter like this:
template <int otherN>
explicit A(A<otherN> const &, typename std::enable_if<otherN >= N>::type* = 0);
With C++20 you can use the requires keyword
With C++20 you can get rid of SFINAE.
The requires keyword is a simple substitute for enable_if!
Note that the case where otherN == N is a special case, as it falls to the default copy ctor, so if you want to take care of that you have to implement it separately:
template<int N> struct A {
A() {}
// handle the case of otherN == N with copy ctor
explicit A(A<N> const& other) { /* ... */ }
// handle the case of otherN > N, see the requires below
template<int otherN> requires (otherN > N)
explicit A(A<otherN> const& other) { /* ... */ }
// handle the case of otherN < N, can add requires or not
template<int otherN>
explicit A(A<otherN> const& other) { /* ... */ }
};
The requires clause gets a constant expression that evaluates to true or false deciding thus whether to consider this method in the overload resolution, if the requires clause is true the method is preferred over another one that has no requires clause, as it is more specialized.
Code: https://godbolt.org/z/RD6pcE
Related
template <typename T, typename... Tc>
constexpr bool ConjuctionofConvertible () noexcept
{
return std::conjunction<std::is_convertible_v<T, Tc>...>::value;
}
Getting error:
Error no return statement in constexpr function
Error template argument for template type parameter must be a type
You just need to use is_convertible instead of the helper variable is_convertible_v:
template <typename T, typename... Tc>
constexpr bool ConjuctionofConvertible () noexcept
{
return std::conjunction<std::is_convertible<T, Tc>...>::value;
}
or
template <typename T, typename... Tc>
constexpr bool ConjuctionofConvertible () noexcept
{
return std::conjunction_v<std::is_convertible<T, Tc>...>;
}
See this possible implementation of conjunction:
template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
^^^^^^^^^
You'll notice that it requires a type (with a value member), not a bool.
Other interesting notes, copied and edited from std::conjuction:
std::conjunction was added in C++17 and so was fold expressions. One reason for using conjuction over a fold expression is that
conjunction is short-circuiting.
If there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.
The short-circuit instantiation differentiates conjunction from fold expressions: a fold expression like (... && Bs::value) instantiates every B in Bs, while std::conjunction_v<Bs...> stops instantiation once the value can be determined. This is particularly useful if the later type is expensive to instantiate or can cause a hard error when instantiated with the wrong type.
There is no need to use std::conjunction in your example, a simple fold-expression should be enough.
#include <type_traits>
template <typename T, typename... Tc>
constexpr bool AllConvertible () noexcept {
return (std::is_convertible_v<T, Tc> && ...);
}
Let's say I have a template function:
template <typename A, typename B>
A fancy_cast(B)
{
return {};
}
The intended usage is something like fancy_cast<int>(1.f).
But nothing stops the user from specifying the second template parameter manually: fancy_cast<int, int>(1.f), which would cause problems.
How can I prevent typename B from being specified and force it to be deduced?
I've come up with this:
// Using this wrapper prevents the code from being
// ill-formed NDR due to [temp.res]/8.3
template <auto V> inline constexpr auto constant_value = V;
template <
typename A,
typename ...Dummy,
typename B,
typename = std::enable_if_t<constant_value<sizeof...(Dummy)> == 0>
>
A fancy_cast(B)
{
return {};
}
It appears to work, but it's extremely cumbersome. Is there a better way?
What about making fancy_cast a variable template?
template <typename A>
struct fancy_cast_t {
template <typename B>
A operator()(B x) const { return x; }
};
template <typename A>
constexpr fancy_cast_t<A> fancy_cast {};
fancy_cast<int>(1.5); // works
fancy_cast<int, int>(1.5); // doesn't work
fancy_cast<int>.operator()<int>(1.5); // works, but no one would do this
This is not the most efficient solution, but you can create a class that has a template parameter for the type to convert to, and then have a constructor template that takes any type. Then if you add an operator T for the type you instantiate the class with you can have that return correct value. That would look like
template<typename T>
struct fancy_cast
{
T ret;
template<typename U>
fancy_cast(U u) : ret(u) {} // or whatever you want to do to convert U to T
operator T() && { return std::move(ret); }
};
int main()
{
double a = 0;
int b = fancy_cast<int>(a);
}
This works because there is no way to specify the template parameter for the constructor since you can't actually call it.
I found a good-looking solution.
We can use a non-type parameter pack, of a type that the user can't construct.1 E.g. a reference to a hidden class:
namespace impl
{
class require_deduction_helper
{
protected:
constexpr require_deduction_helper() {}
};
}
using require_deduction = impl::require_deduction_helper &;
template <typename A, require_deduction..., typename B>
A fancy_cast(B)
{
return {};
}
1 We do have to leave a loophole for constructing a deduction_barrier, otherwise the code would be ill-formed NDR. That's why the constructor is protected.
I want to make a trait / interface, e.g. is_good, and implement my generic functions targeting its specification.
I come up with the following code, helped by some online resources regarding SFINAE.
template<class C>
struct is_good: std::false_type
{
};
template<>
struct is_good<A<double, 2>>: std::true_type
{
};
template<>
struct is_good<B<double, 2, 3>>: std::true_type
{
};
template<template<class> class C, class T>
using check_constraints = std::enable_if_t<C<T>::value, bool>;
}
My generic functions are defined like this:
template<class T, template<class> class C = is_good,
typename = check_constraints<C, T>>
void compute(const T& t) {}
At usage
// works
compute(A<double,2>());
compute(B<double,2, 3>());
// should not compile.
compute(B<double,5, 6>());
However, it seems a bit cumbersome. And I have to prepend
template<class T, template<class> class C = is_good,
typename = check_constraints<C, T>>
to all my functions that I plan to genericize.
Are there better ways?
Update
The basic of this question is, give that I know the body of my functions work with type A & B & C, how can I define my functions?
For example, in other languages, perhaps you can do
using T = Union{A, B, C};
void compute(T t){...}
# works with A
compute(A());
# works with B
compute(B());
no matter how A, B, C looks like.
Not clear in your question. But if is_2d has to be in all functions, why not move it to check_constraints? Ref: https://gcc.godbolt.org/z/s9Jql-
template<class T>
using check_constraints = std::enable_if_t<is_2d<T>::value, bool>;
template<class T, typename = check_constraints<T>>
void compute(const T& t){
works(t);
}
Update
If you just want is a list of allowed types, the following is better, since you have c++17. https://gcc.godbolt.org/z/kJN7hu
#include<type_traits>
template<class... Types>
struct TypeContainer{
template<class T>
using contains = typename std::disjunction< std::is_same<T,Types>... >::type;
};
using allowed_types = TypeContainer<
int,
float
>;
template<class T, typename = std::enable_if_t<allowed_types::contains<T>::value>>
void compute(const T& t);
void foo() {
int i;
compute(i);
//char c; compute(c);
}
However, it seems a bit cumbersome. And I have to prepend
template<class T, template<class> class C = is_good,
typename = check_constraints<C, T>>
to all my functions that I plan to genericize.
Are there better ways?
I consider C-style macros distilled evil but, for a problem like this, I suppose that a macro for that template signature can be reasonable.
Anyway, I suggest a little partial improvement.
With your actual function-style, with the deducible type (T) before the default template-template parameter (C)
template<class T, template<class> class C = is_good,
typename = check_constraints<C, T>>
void compute(const T& t) {}
if you want explicit a template-template parameter different from the default one (is_good), you have to explicit both the deducible type and the template-template parameter
compute<B<double, 2, 3>, is_good_i_hope>(B<double, 2, 3>);
losing the deduction of T from the function argument.
But if you express the template-template parameter before the deducible type
template <template<typename> class C = is_good, typename T,
typename = check_constraints<C, T>>
void compute (T const &)
{ }
you, when you want explicit a template-template parameter different from the default one, can explicit only C
compute<is_good_i_hope>(B<double, 2, 3>);
maintaining the deduction of T.
Once you have your traits, you might simply use SFINAE:
template<class T>
std::enable_if_t<is_good<T>>
compute(const T& t) {}
or
template<class T, std::enable_if_t<is_good<T>, int> = 0>
void compute(const T& t) {}
Recently I designed meta-types and the possible operations that would allow compile-time type concatenations:
#include <tuple>
template<template<typename...> typename T>
struct MetaTypeTag
{};
/*variable template helper*/
template<template<typename...> typename T>
constexpr MetaTypeTag<T> meta_type_tag = {};
template<typename T>
struct TypeTag
{};
/*comparison*/
template<typename T>
constexpr bool operator==(TypeTag<T>, TypeTag<T>) { return true; }
template<typename T, typename U>
constexpr bool operator==(TypeTag<T>, TypeTag<U>) { return false; }
/*variable template helper*/
template<typename T>
constexpr TypeTag<T> type_tag = {};
template<template<typename...> typename T, typename... Ts>
constexpr TypeTag<T<Ts...>> combine(MetaTypeTag<T>, TypeTag<Ts>...)
{
return {};
}
int main()
{
constexpr auto combined_tag = combine(meta_type_tag<std::tuple>, type_tag<int>, type_tag<float>);
static_assert(combined_tag == type_tag<std::tuple<int, float>>, "");
}
The std::tuple without template arguments cannot be used as a type, but may still appear in the template template parameter.
Now if we try to go one step further, the question is whether there is any way to unify struct MetaTypeTag and struct TypeTag, since they are both empty classes with one template parameter, or at least it could be possible to use the same variable template type_tag but redirect to a different class depending on the type category? So I would imagine something like this:
template<???>
constexpr auto type_tag = ????{};
//use with 'incomplete type'
type_tag<std::tuple> //MetaTypeTag<std::tuple>
//use with regular type
type_tag<int> //TypeTag<int>
I tried all possible ways - redefinition, explicit specialization, partial specialization, optional template parameters, conditional using alias, but none worked. I had hoped C++17's template<auto> would help, but it turns out that one is for non-type only.
the question is whether there is any way to unify struct MetaTypeTag and struct TypeTag, since they are both empty classes with one template parameter
I don't thinks so.
The best I can imagine to simplify a little (very a little) your code is define a couple of overloaded constexpr function, say getTag()
template <typename T>
auto constexpr getTag ()
{ return TypeTag<T>{}; }
template <template <typename ...> typename T>
auto constexpr getTag ()
{ return MetaTypeTag<T>{}; }
so you can call getTag<T>() where T is either a type or a template.
So you can call combine() as follows
constexpr auto combined_tag
= combine(getTag<std::tuple>(), getTag<int>(), getTag<float>());
But I don't think is a great improvement.
I've been attempting a little SFINAE at making a way to determine if a generic type T has a copy constructor I can use. Here is where I currently am.
template <bool statement, typename out>
struct Failable
{
typedef out Type;
};
//This class is only used to insert statements that
//could encounter substitution failure
template <typename O>
struct COPY
{
template <typename T>
typename Failable<true == sizeof(&T::T(const T&)), char>::Type copy(int)
{}
template <typename T>
typename Failable<true, int>::Type copy(...)
{}
};
However, this is also where I'm kinda stuck. &T::T(const T&) is obviously an invalid statement, as we can't provide an argument list with a pointer-to-member, even if a p-t-m-function.
I could always try to specify some sort of void (T::*ptmf)(const T&) = &T::T, and hope it implicitly determines the right overloaded constructor to put into the pointer to member function, but that also implies that Constructors have a specific return type, which I would have to specify.
Does anyone else have any ideas with which I can trek forth? (I also need to apply a similar concept to checking for an assignment operator.)
Thanks in advanced.
You could use std::is_copy_constructible and std::is_assignable for this.
You can do with your code, just need to modify it a little bit.
template <bool statement, typename out>
struct Failable
{
typedef out Type;
};
template <typename O>
struct COPY
{
static O MakeO();
template <typename U> // U and T are the same type
static typename Failable<(sizeof U(MakeO())), char>::Type copy(int);
template <typename U>
static typename Failable<true, int>::Type copy(...);
enum { value = sizeof(char) == sizeof( copy<O>(0) ) };
};