How to make traits to accept a parameter pack? - c++

I define some type traits like this:
template <typename T>
struct has_something
{
static constexpr bool value = false;
};
template <>
struct has_something<int>
{
static constexpr bool value = true;
};
template <typename T>
constexpr bool has_something_v = has_something<T>::value;
And a function template which is has_something_v is a requirement for function parameter:
template <typename T, typename = std::enable_if_t<has_something_v<T>>>
void some_function(const T temp)
{
}
When i call it with wrong type:
struct wrong_type
{
};
void f ()
{
some_function(wrong_type());
}
compiler give me a proper error message:
/tmp/untitled/main.cpp:23: candidate template ignored: requirement 'has_something_v<wrong_type>' was not satisfied [with T = wrong_type]
but when i called with another template function:
template <typename ...T, typename = std::enable_if_t<has_something_v<T...>>>
void some_function(const T... args)
{
(some_function(args), ...);
}
void f ()
{
some_function(1, 2, a());
}
compiler give me really bad and confusing error message because i don't have a parameter pack acceptable traits :
Compiler error message
And if i remove std::enable_if from last template function, everything work fine until i send a wrong_type type to function which result is in crashing program.
For parameter pack, i wrote this:
template <typename ...T>
struct has_something
{
static bool value;
static constexpr bool c(T... args)
{
value = (args && ...);
return value;
}
};
template <>
struct has_something<int>
{
static constexpr bool value = true;
};
template <typename ...T>
const bool has_something_v = has_something<T...>::value;
But it still fail.
How could i write a acceptable parameter pack type traits ?

If you want to make the trait accept a parameter pack and it's value be true only when the parameter pack has a single type and that type is int you need to change only little on your code:
#include <iostream>
#include <type_traits>
template <typename...T>
struct has_something : std::false_type {};
template <>
struct has_something<int> : std::true_type {};
template <typename... T>
constexpr bool has_something_v = has_something<T...>::value;
int main() {
std::cout << has_something_v<int>;
std::cout << has_something_v<double>;
std::cout << has_something_v<int,double>;
}
Using std::true_type and std::false_type makes traits a bit shorter to write. I only had to make the trait accept a parameter pack, the specialization can stay the same.
Last but not least you should pick a better name. For example is_one_int would be much better than something.
PS: SFINAE can be used to create compiler errors, but often a simple static_assert is the better choice to get a clean message:
template <typename...T>
void foo(T...args) {
static_assert( is_one_int_v<T...>, " put nice message here");
}
SFINAE is the tool of choice when you want to disambiguate different overloads, but if a function should simply fail without alternative then a static_assert is simpler.

Related

C++ template argument without a name

I have come across the following code snippet:
template <typename T, typename = void>
struct test {
int t = sizeof(T);
};
I know that in typename = void, void is a default argument but it doesn't have a name! What is it useful for and what does it even mean?
This is used for specializations in conjunction with SFINAE. Doing this allows you to have code like
template <typename T, typename = void>
struct test {
int t = sizeof(T);
};
template <typename T>
struct test<T, std::enable_if_t<std::is_integral_v<T>>> {
// ^^ this part "fills in" the void ^^
int t = 42;
};
template <typename T>
struct test<T, std::enable_if_t<std::is_floating_point_v<T>>> {
// ^^ this part "fills in" the void ^^
int t = 21;
};
int main()
{
test<int> i;
std::cout << i.t << "\n";
test<double> d;
std::cout << d.t;
}
which outputs
42
21
Without the typename = void, we would not be able to add these specializations because there would be no second parameter the enable_if_t part could "fill in".
I personally like to see it as a case of default template (type)argument to sfinae out overloads which does not meet certain criteria.
First thing first, giving name to a default template argument is just fine, so the following is correct:
template <typename T, typename sometype= void>
struct test {
int t = sizeof(T);
};
In the above case , clearly the type argument sometype is not used anywhere in the struct test. But what if instead of setting default value to void, we set it using some compile time conditions so that the template function is only valid for integral types like so ?
(borrowing code's from nathan's answer)
template <typename T>
struct test<T, typename sometype = std::enable_if_t<std::is_integral_v<T>>> {
int t = 42;
};
If the T has type integral then sometype is defined otherwise the given template is ignored making use of sfinae.
Additionally, you can drop "sometype" to write :
template <typename T>
struct test<T, typename = std::enable_if_t<std::is_integral_v<T>>> {
int t = 42;
};
Finally compare this with the default values used in function declarations:
void foo(int = 9); //Function declaration can have default values without names too.
void foo (int a )
{
//some code
}

Constexpr if testing one template parameter

If I have a class that has two template parameters, is there any way to branch an if constexpr on just one of those parameters? In the following example I can test if both parameters match, but I would like a single way of matching any version of MyTemplateClass that has char as its first parameter.
#include <iostream>
#include <type_traits>
template<typename T,int32_t N>
class MyTemplateClass
{
};
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr(std::is_base_of_v<MyTemplateClass<char,64>, C>) // how do I remove the hardcoded 64?
{
// test passed!
}
else
{
static_assert(false);
}
}
int main()
{
MyTemplateClass<char, 64> passesObj;
MyTemplateClass<char, 128> shouldPassObj;
MyTemplateClass<wchar_t, 64> failsObj;
DoStuff(passesObj); // passes correctly
DoStuff(shouldPassObj); // currently fails, want to make pass
DoStuff(failsObj); // correctly fails
}
You're too focused on new language features instead of old techniques. If you want a template function that only works if the type given is any instance of MyTemplateClass which takes as its first parameter char, then write that:
template<int32_t N>
void DoStuff(const MyTemplateClass<char, N> &myObj)
{
// test passed!
}
This is standard template argument deduction, not C++17 if constexpr gymnastics. This will fail to be called for any type other than one generated from the MyTemplateClass template instantiated with char.
It's possible to have a more generalized solution using template parameter packs, but since all parameters in a pack must be of one kind of template parameter (type, value, or template), you wouldn't be able to mix type and value template parameters in the same pack:
template<typename ...Args>
void DoStuff(const SomeClass<known, params, ...Args> &myObj);
That would only work if the extra arguments are type parameters, not value or template parameters. You could make Args a value parameter pack with auto, but then they couldn't be types. C++ has no mechanism to make a template parameter of known kind.
If DoStuff() receive only MyTemplateClass objects, you can use template deduction
template <typename T, std::int32_t N>
void DoStuff (MyTemplateClass<T, N> const & myObj)
{
if constexpr ( std::is_same_v<char, T> )
{
// test passed!
}
else
{
// not passed
}
}
Another solution could be add a constexpr value in MyTemplateClass
template <typename T, std::int32_t N>
class MyTemplateClass
{ using type = T };
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr ( std::is_same_v<typename C::type, char> )
{
// test passed!
}
else
{
// not passed
}
}
A third solution could be a custom type-trait to extract N
template <typename>
struct getT;
template <typename T, std::int32_t N>
struct getT<MyTemplateClass<T, N>
{ using type = T };
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr ( std::is_same_v<typename getT<C>::type, char> )
{
// test passed!
}
else
{
// not passed
}
}

Check the existence of a template function

How can I check the existence of a template function like this: Checking if reader struct has read arithmetic value
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
I use a checker like this:
template <typename T>
struct test_read {
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
};
But the compiler complains:
error: wrong number of template arguments (1, should be 2)
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
Please give me your advice on that.
Thank you.
Update: Here is the final version I got after discussion, I hope everyone will find it helpful for your code
struct not_reader {
};
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
template<class T, class Elem>
struct has_read {
private:
template<class C, typename=void>
struct test_read : std::false_type {
};
template<class C>
struct test_read<C, typename std::enable_if<std::is_convertible<decltype(std::declval<C>().template read<Elem>()), Elem>::value>::type>
: std::true_type {
};
public:
using type = typename test_read<T>::type;
static constexpr bool value = test_read<T>::value;
};
static_assert(has_read<reader, int>::value, "reader should have int read()");
static_assert(!has_read<not_reader, int>::value, "not_reader should not have int read()");
You forgot template before read()
static constexpr auto value
= std::is_convertible<
decltype(std::declval<T>().template read<int>()),int>::value;
// .................................#########
But I don't think that your code can check " if reader struct has read arithmetic value": try calling test_read with type int and you should get a compilation error.
The following is an example of an alternative solution
#include <type_traits>
struct reader
{
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read()
{ return {}; }
};
template <typename, typename = void>
struct readTypeRet
{ using type = void; };
template <typename T>
struct readTypeRet<T, decltype(std::declval<T>().template read<int>(), void())>
{ using type = decltype(std::declval<T>().template read<int>()); };
template <typename T>
struct test_read
: public std::is_convertible<typename readTypeRet<T>::type, int>
{ };
int main ()
{
static_assert(test_read<reader>::value == true, "!");
static_assert(test_read<int>::value == false, "!");
}
To briefly restate your problem in terms that are a bit clearer:
You have some function that will return T if T satisfies is_arithmetic, otherwise it returns void
You want to assert that calling this function with int will return a type convertible to int
I think the shortest path to fix your code is to take advantage of std::result_of (C++11/14, use std::invoke_result_t in C++17):
template<class T>
struct test_read {
static constexpr auto value = std::is_convertible<
typename std::result_of<decltype(&T::template read<int>)(T)>::type, int
>::value;
};
Live Demo
Some notes about this solution:
When specifying the read member function of T (reader), we need to use the template keyword to inform the compiler that the name reader is a template.
Use of result_of requires a function-like syntax of F(Args), so here we are getting the type of reader::read as the F portion, and then passing reader as the Args portion
we must pass an instance of T (reader) to read because it is a member function (rather than static or free), and member functions implicitly take a reference to the instance of the class they're being called on.

Check if a function template is unary

I am trying to check if a function argument passed is unary or not, something like so
template <typename Func>
using EnableIfUnary = std::enable_if_t<std::is_same<
decltype(std::declval<Func>()(std::declval<const int&>())),
decltype(std::declval<Func>()(std::declval<const int&>()))>::value>;
template <typename Func, EnableIfUnary<Func>* = nullptr>
void do_something(Func func) { ... }
// and use like so
template <typename Type>
void foo(Type) { cout << "foo(Type)" << endl; }
template <typename Type>
void bar(Type) { typename Type::something{}; }
int main() {
do_something(foo);
return 0;
}
Is there a better way to check if a function is unary? My current approach doesn't work when the function pass in (foo() in my example) uses the type in a way that would not work with ints.
In the above case foo is legal and bar isn't, since there is no type named something in int (which is what the enable if checks for)
template<typename...>
struct is_unary_function : std::false_type {};
template<typename T, typename R>
struct is_unary_function<R(*)(T)> : std::true_type {};
Live Demo

Variadic templates - incomplete type

Having this code:
template<class ...Args>
struct Are_Same
{
enum {value = Are_Same<Args...>::value};
};
template<class A,class... C>
struct Are_Same<A,C...>
{
enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE
};
template<class A,class B>
struct Are_Same<A,B>
{
enum {value = std::is_same<A,B>::value};
};
I'm getting error from gcc 4.6.1:
error: incomplete type 'Are_Same' used in
nested name specifier.
I thought that by doing Are_Same<A,C...>::value I will invoke recursive call which at the end will simply expand to Are_Same<A,B>. Obviously it's not the case. Anyone knows where am I making mistake?
I think that the definitions of the templates are wrong, in both cases you are triggering exact recursion. I would have expected the compiler to die with some stackoverflow inside the compiler but a different error is produced...
An implementation of the are_same variadic template could be:
template <class... Args> // base (optional to declare the template)
struct are_same;
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = is_same<A,B>::value && are_same<A,Args...>::value;
};
template <class A, class B> // stop condition
struct are_same<A,B> {
static const bool value = is_same<A,B>::value;
};
Note that in the recursion step, one argument is dropped from the list of arguments, so that the new problem to resolve is a reduced version of the original. This type of template metaprogramming is quite related to recursion, and the same rules apply, to be able to use recursion you need to ensure that each recursive step gets you closer to a solution. In this particular case, given a list of N potentially same types, each step reduces the problem to finding whether N-1 types are the same.
You can use alternatively, as stop condition (replacing the former one) a degenerate version of the are_same problem:
template <class A>
struct are_same<A> {
static const bool value = true;
};
Which is degenerate in the sense that it does not really make sense asking whether a single type *are_same*, but for different metaprogramming tasks it could be appropriate.
A different potentially more efficient algorithm (I am not sure whether the compiler will avoid the instantiation of the template in the recursion step above) that does not depend on is_same could be:
template <class... Args>
struct are_same;
template <class A, class... Args>
struct are_same<A,A,Args...> { // recursion
static const bool value = are_same<A,Args...>::value;
};
template <class A, class B, class... Args>
struct are_same<A,B,Args...> { // cut, A and B are not the same
static const bool value = false;
};
template <class A>
struct are_same<A> { // end of recursion
static const bool value = true;
};
In this case, the compiler will prefer the recursion to the cut steps whenever the two types are the same, so we need not check is_same internally. At the same time, if the compiler goes into the cut step, we don't need to process the rest of the type list, as we already know the answer.
I would do it like this :
#include <type_traits>
#include <iostream>
template <class... Args>
struct are_same
{
static const bool value=true;
};
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = std::is_same<A,B>::value && are_same<B,Args...>::value;
};
int main()
{
std::cout<< std::boolalpha << are_same< int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, double, int >::value << std::endl;
}
Probably simplest implementation could be like this:
template <typename... TList>
struct are_same { constexpr static bool value = false; };
template <typename T, typename... TList>
struct are_same<T, T, TList...> {
constexpr static bool value = are_same<T, TList...>::value;
};
template <typename T>
struct are_same<T> { constexpr static bool value = true; };
Alternatively you can replace stop condition with
template <typename T>
struct are_same<T, T> { constexpr static bool value = true; };
But the first one is more general because are_same<type>::value == true. Another question is what should be are_same<>::value equal to. This gives you false but it is not a big deal to add one more template specialisation like this.
template <>
struct are_same<> { constexpr static bool value = true; };