I have a scoped enum:
enum class E
{ A, B, C };
Now I want to have a function, which accepts a value of that scoped int OR an int itself.
It should be something like:
template <typename T, std::enable_if_t<std::is_same<T, enum E **OR** int>::value, int> = 0 >
void foo(T value);
But I don't know how to deal with the concept of OR in C++ templates. I know std::is_convertible, however I don't even know, if I could use it here, because you can only static_cast scoped enums to int.
But anyway I don't want to accept any type that is convertible to int but only types of that single enum or int.
Overload seems the simplest:
void foo(int value);
void foo(E value) { foo(static_cast<int>(value); } // Or specific code
template <typename T> void foo(T) = delete; // To forbid type convertible to int
else you can use SFINAE
template <typename T>
std::enable_if_t<std::is_same<int, T>::value || std::is_same<E, T>::value>
foo(T value);
Because std::is_same<...>::value is a boolean, you can simply use || operator with 2 std::is_same<...>::value :
template <typename T, std::enable_if_t<std::is_same<T, enum E>::value || std::is_same<T, int>::value, int> = 0 >
void foo(T value);
std::is_same instantiations define a constexpr implicit bool conversion, so you can instantiate them and perform logical OR with ||. In C++17, you will also be able to use std::disjunction to similar effect, although this will likely compile slower for only two type traits. Example of both:
#include <type_traits>
enum class E
{ A, B, C };
template <typename T, std::enable_if_t<
std::is_same<T, E>{} || std::is_same<T, int>{},
int> = 0>
void foo(T){
}
//in C++17, you can also do this:
template <typename T, std::enable_if_t<
std::disjunction<std::is_same<T, E>, std::is_same<T, int>>{},
int> = 0>
void bar(T){
}
int main() {
foo(E::A);
foo(0);
//foo('A'); fails
bar(E::A);
bar(0);
//bar('A'); fails
return 0;
}
std::disjunction is the logical OR template you were wondering about (although I recommend using || with std::is_same for your case). Interestingly, std::disjunction even performs logical short-circuiting of template instantiations, just like the humble old || operator does in a runtime context. I believe recent versions of libc++ are already shipping with std::disjunction. If your <type_traits> implementation doesn't have it yet, the example implementation at cppreference works just fine for me. If you get the chance, you should see how it works. It's rather clever!
Related
#define BINDINGTEMPLATE template<typename T, typename = typename std::enable_if_t < std::is_same_v<typename std::decay_t<T>, int> || std::is_same_v<typename std::decay_t<T>, std::string> || std::is_same_v<typename std::decay_t<T>, char>>>
Is something like this bad practice?
I am using this function template many times within the same class.
BINDINGTEMPLATE
void myFunction(int x, int y)
{
// do something specialised based on input template
}
For instance, I need to use it in many functions, like this one :
Yes, it's a bad practice. Don't use macros for something that can easily be done without them.
You can move that long SFINAE condition into something like
template <typename T> using foo = std::enable_if_t<...>;
And then you can write simply:
template <typename T, typename = foo<T>>
void myFunction(int x, int y)
Alternatively, you could put the condition into a constexpr function or variable template, then write enable_if_t every time you use it.
Also you could use a concept (requires C++20):
template <typename T>
concept foo = std::is_same_v<typename std::decay_t<T>, int> || etc;
template <foo T>
void myFunction(int x, int y)
Note that this use of enable_if_t (regardless of whether you're using a helper using or not) is not very robust, as the user can circumvent it by explicitly specifying the second template parameter.
A better approach is:
template <typename T, std::enable_if_t<..., std::nullptr_t> = nullptr>
void myFunction(int x, int y)
In addition to being foolproof, this also lets you overload the function based on different traits.
The concepts also solve both problems.
You can achieve the same with a type-trait:
template <typename T>
using enable_if_int_or_string_or_char = std::enable_if_t <
std::is_same_v<typename std::decay_t<T>, int>
|| std::is_same_v<typename std::decay_t<T>, std::string>
|| std::is_same_v<typename std::decay_t<T>, char>>;
Instead of the macro you can then use enable_if_int_or_string_or_char<T>. Macros have severe downsides, so the best macro is the one that you do not need to use.
IMHO, macros are distilled evil.
So, yes: (IMHO) is bad practice.
I propose another way (just for fun: the other answers show good solutions) to avoid it.
You can declare a foo() (SFINAE enabled/disabled function) as follows
template <typename T>
std::enable_if_t<std::is_same_v<std::decay_t<T>, int>
|| std::is_same_v<std::decay_t<T>, std::string>
|| std::is_same_v<std::decay_t<T>, char>> foo ();
you can write your final function using it
template <typename T>
decltype(foo<T>()) bar (int, int)
{ }
The following is a full compiling example
#include <string>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_same_v<std::decay_t<T>, int>
|| std::is_same_v<std::decay_t<T>, std::string>
|| std::is_same_v<std::decay_t<T>, char>> foo ();
template <typename T>
decltype(foo<T>()) bar (int, int)
{ }
int main ()
{
bar<int>(0, 0); // compile
// bar<long>(0, 0); // compilation error
}
This is an example of trait checking if the type is one of the types you look for:
template <typename T>
struct is_int_string_char
{
static const bool value = std::is_same<std::decay<T>::type, int>
|| std::is_same<std::decay<T>::type, std::string>
|| std::is_same<std::decay<T>::type, char>;
};
You can use it as a constant at compilation type with is_int_string_char<MyType>::value. In your case (if I well understand your code), you can simplify it with:
template <typename T, typename U = std::enable_if<is_int_string_char<T>>
Since C++ provides many other options to solve this, one should not go for macros(IMO).
For instance, you could make a variable template, where you can specify that any of the passed template Types is same as myFunction's template type T, and SFNIAE the solution.
Thanks to c++17's std::disjunction, by which this idea can be generalized for any arbitary number of Types, with less noisy!
#include <string>
#include <type_traits> // std::enable_if_t, std::is_same, std::disjunction_v
template<typename T, typename... Types>
constexpr bool isAcceptableType = std::disjunction_v<std::is_same<T, Types>...>;
template <typename T>
auto myFunction(T x, T y) -> std::enable_if_t<isAcceptableType<T, char, std::string, int>>
{
// do something
}
See a demo
People yelling macros are evil are similar to those yelling goto is evil.
Since SFINAE until C++20 is really ugly, macros are a good way to make it more user friendly. But, the macro you provide is indeed bad practice: It's way too long.
I regularly use the following two macros REQUIRES and CONCEPT, named after C++20 concepts, for quite some time already:
#define REQUIRES( ... ) typename std::enable_if< __VA_ARGS__, bool >::type = false
template< typename T > constexpr bool always_true = true;
#define CONCEPT( ... ) always_true< __VA_ARGS__ >
Two notes:
Although the macro actually only take one argument, I use __VA_ARGS__ so that commata are handled correctly when used. Otherwise, people would have to double parenthise expressions involving a comma.
The always_true template is similar to void_t, but I wrote these macros before I knew about void_t.
Usage examples:
template< typename T, REQUIRES( std::is_arithmetic<T>::value && sizeof(T)>=4 ) >
void f( T t );
template< typename T,
REQUIRES( std::is_integral<T>::value ),
REQUIRES( sizeof(T)>=4 ) >
struct BigIntConcept;
template< typename T, REQUIRES( CONCEPT( BigIntConcept<T> ) ) >
void f( T t );
[+] One of the greatest benefits of this macro is: Even people which do not understand SFINAE can read/understand it, but also use it.
[-] The only downside I found so far is: The macros work nearly only for SFINAE in the template-parameter-declaration place (I am sure there is a word for this).
I was wondering if it was possible for a template (or any other tool you might recommend)to only take a type from a list of specific types (like an enum, but with already existing types).
More specifically, if I have 3 classes, class A, class B and class C, and I'd like a function to be able to take any of those three classes as argument (but no other class than the 3), what should I do ?
Should I use a template (if so how should I use it), or are there other tools for me to use ?
The options that comes to mind for me are SFINAE, static_assert or overloading. What option is best would depend on the function itself.
SFINAE method
#include <type_traits>
template <typename T, typename std::enable_if<
std::is_same<A, T>::value ||
std::is_same<B, T>::value ||
std::is_same<C, T>::value, int>::type = 0>
void foo(T t) {
// ...
}
Static assert
template <typename T>
void foo(T t) {
static_assert(std::is_same<A, T>::value ||
std::is_same<A, T>::value ||
std::is_same<A, T>::value, "Must pass A, B or C");
// ...
}
Overloading
void foo(A a) {
// ...
}
void foo(B a) {
// ...
}
void foo(C a) {
// ...
}
Why do you only want this to work for three classes? In general, it's a better idea to keep your interfaces open and support any types that come along (within reason, of course).
If you're sure you want to do this, then a custom type trait and enable_if is probably the best route.
#include <iostream>
#include <type_traits>
template <typename T>
struct is_my_special_type;
template <>
struct is_my_special_type<int> {
static const bool value = true;
};
template <>
struct is_my_special_type<long> {
static const bool value = true;
};
template <typename T, typename std::enable_if<is_my_special_type<T>::value, int>::type = 0>
void foo(T val) {
std::cout << val << '\n';
}
int main() {
foo(10);
foo(10l);
foo(10.0); // won't compile unless you comment this line out
return 0;
}
You can add types through template specialization of my_special_type. You could also use static_assert instead of enable_if if you wanted.
Constraining template parameters to only be of specific types, or types meeting certain requirements, is part of what C++ Concepts is about.
Concepts are a future feature of the C++ language (coming to the official standard perhaps in C++20), which have been specified in a TS (technical specification) - making them a possible extension for compilers to implement if they wish.
For a short introduction-that's-not-intended-as-such, watch:
What makes a good C++ concept? / Bjarne Stroustrup, CppCon 2016
Have a function which can accept 4 different types. And their implementations are very similar.
template< typename T >
void foo( std::string &&name, T value ){
//...
}
typename T must be one of 4 predefined types. Other typenames is not acceptable, and should be restricted at compile-time.
Is it possible to write in C++?
I can think of at least three ways, off the top of my head.
First:
namespace internal {
template< typename T >
void foo_impl( std::string &&name, T value ) {
// actual implementation here
}
}
void foo(std::string &&name, SpecialType1 value ) {
internal::foo_impl(std::forward<std::string>(name), value);
}
void foo(std::string &&name, SpecialType2 value ) {
internal::foo_impl(std::forward<std::string>(name), value);
}
Second:
template< typename T >
typename std::enable_if<std::is_same<T, SpecialType1>::value ||
std::is_same<T, SpecialType2>::value >::type
foo( std::string &&name, T value ){
//implementation here
}
Third:
template< typename T >
void foo(std::string &&name, T value){
static_assert(std::is_same<T, SpecialType1>::value ||
std::is_same<T, SpecialType2>::value,
"wrong template argument");
//implementation here
}
Demo
<type_traits> let you generalize your logic into class template. How this works is we take template parameter T and parameter pack of Ts and we start checking if T is same to Head of the list of types. If match is found, we inherit from std::true_type and we are done. If no match is found, we pop the head and recursively instantiate same template with tail of Ts. Eventually, if no match is found at all, parameter pack size will drop to zero and the compiler will instantiate base template class that inherit from std::false_type. Please check this video for much better and in dept explanation by Mr. Walter E. Brown.
template<class T, class...> struct is_any_of: std::false_type{};
template<class T, class Head, class... Tail>
struct is_any_of<T, Head, Tail...>: std::conditional_t<
std::is_same<T, Head>::value,
std::true_type,
is_any_of<T, Tail...>>
{};
Now we can SFINAE over, using enable_if in almost English wording.
#include <type_traits>
#include <string>
template<
class T,
class = std::enable_if_t<is_any_of<T, int, float, unsigned, double>::value>
>
void foo(std::string &&str, T value) {}
int main()
{
foo(std::string{"hello"}, 3);
foo(std::string{"world"}, '0'); //compile-time error
}
SFANIE is a language feature, a tool, that's used or abused some say to achieve what you ask for,
The standard library component std::enable_if allows for creating a substitution failure in order to enable or disable particular overloads based on a condition evaluated at compile time.
FYI http://en.cppreference.com/w/cpp/language/sfinae.
Note that std::conditional_t<> and std::enable_if_t<> are shorthanded from std::conditional<>::type and std::enable_if<>::type respectively. You could simple replace these in code, but should put typename keyword before enable_if then.
One thing I have seen people done is using std::enable_if. I don't know exactly what your 4 types are, so this is an example with two types int and float.
using myType = std::enable_if<
std::is_same<int, T>::value ||
std::is_same<float, T>::value, T >::type;
myType exists only if T is exactly int or float. Hope that helps!
I know I am late to the party but maybe someone will find this useful.
As of C++20 one can use constraints and concepts to filter possible typenames in a template.
The syntax goes like this:
template <typename T>
requires std::is_integral_v<T>
auto func(T any_integer) {
...
};
// Or, you can also do:
template <typename T>
concept Integer = std::is_integral_v<T>;
template <Integer T>
auto func(T any_integer) {
...
};
// Or, an even better approach:
auto func(Integer auto any_integer) {
...
};
I prefer the last one since it's nice and clean. But, that's just my opinion.
Following are some valid calls to func():
func(int{}); // OK!
func(long{}); // OK!
func(bool{}); // Yes, it's OK too!
func(char{}); // Yes, it's OK too!
Now, some invalid calls to func():
func(double{}); // Compiler error!
func(std::string{}); // Compiler error!
The compiler will also give a clean error message depending on your usage:
note: candidate: ‘template requires is_integral_v void func(T)’
or
note: candidate: ‘template requires Integer auto func(T)’
or
note: candidate: ‘template requires Integerauto:15 auto func(auto:15)’
References:
https://en.cppreference.com/w/cpp/types/is_integral
Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.
I want to create a simple integer range checker and converter using c++ templates.
The code looks like this:
// D is the "destination" type and S the "source" type
template <class D, class S>
inline D SafeConvert( S value );
template <class S>
inline int SafeConvert<int>( S value ) {
ASSERT( value >= S(INT_MIN) && value <= S(INT_MAX) );
return int(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments
template <class S>
inline size_t SafeConvert<size_t>( S value ) {
ASSERT( value >= S(0) && value <= S(size_t(-1)) );
return size_t(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments
// ...
void test() {
size_t v = INT_MAX+1;
int iv = SafeConvert<int>(v);
}
However I have the following come compilation errors:
error C2768: 'SafeConvert' : illegal use of explicit template arguments
My question is how to tell the compiler that I want to specialize only the D class ?
Thanks.
You can't partially specialize function templates. You need to mimic it with a class wrapper or use standard function overloading. An example of mimicing:
template <typename T1, typename T2>
struct processor;
template < typename T1, typename T2 >
T1 fun(T2 t2) { return processor<T1,T2>::apply(t2); }
template < typename T2 >
struct processor<int,T2>
{
static int apply(T2 t2) { .... }
};
...etc...
It's going to be a bother, and a hell to maintain.
Normally I would advise using the numeric_limits:
template <class D, class S>
D SafeConvert(S value)
{
ASSERT(value >= std::numeric_limits<D>::min()
&& value <= std::numeric_limits<D>::max());
return static_cast<D>(value);
}
However there is a warning emitted by the compiler whenever you compare a signed integer with an unsigned one... (never really understood this by the way)
So, instead of reinventing the wheel, I shall advise the use of Boost.NumericConversion and notably: boost::numeric_cast<>.
It's guaranteed to be performance free when the check is not required (ie the destination type is bigger than the source type) and otherwise perform the necessary checks.
Write a structure SafeConverter<T, S> that is used by SafeConvert. Better than partial specialization would be using std::numeric_limits, or even boost::numeric_cast, which already implements range checking in a more sophisticated way.
The latter could be implemented as follows:
template<typename T, typename S>
struct numeric_converter {
static T convert(const S& val);
}
template<typename T, typename S>
T numeric_cast(const S& val) {
typedef numeric_converter<T, S> converter;
return converter::convert(val);
}
Just write SafeConvert<size_t, S> instead of SafeConvert<size_t>, I think, to specialise only the second parameter. Noah Roberts is correct, too, on the point of partial specialisation of functions versus types.