I want to write a function that will do an operation based on the types, not arguments, of a function. As such, the function receives no template based arguments. The general gist is something like this:
#include <iostream>
void func() {
std::cout<<"End marker\n";
}
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(T)<<"\n";
func<T...>();
}
int main() {
func<int, int, int>();
}
Which, of course, doesn't compile. I've tried doing this:
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(T)<<"\n";
if( sizeof...(T)!=0 )
func<T...>();
}
This does not work, however. The func<T...> may not get evaluated, but it does need to be compilable.
Is there a way to do this that I'm missing?
You can make your original setup work by making func a "template function" (that doesn't actually use the template) like:
template<int = 0>
void func() {
std::cout<<"End marker\n";
}
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(Type)<<"\n";
func<T...>();
}
And your second one can work by using if constexpr, so func<>() isn't compiled.
With fold-expression (in C++17), you might do:
template <typename... Ts>
void func()
{
((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), ...);
std::cout << "End marker\n";
}
That can even be done in C++11, but it would be more verbose:
template <typename... Ts>
void func()
{
const int dummy[] = {((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
std::cout <<"End marker\n";
}
Demo
You can make the non-template function func into a variadic template function which accepts zero template arguments. Then let SFINAE move away this template function when the number of arguments is not zero.
Following should work:
#include <iostream>
#include <type_traits>
template <typename... Ts>
typename std::enable_if<sizeof...(Ts) == 0>::type func() {
std::cout<<"End marker\n";
}
template <typename T, typename... Ts>
void func() {
std::cout << "Type sizeof " << sizeof(T) << "\n";
func<Ts...>();
}
int main() {
func<int, int, int>();
}
However, please note that:
(8) The validity of a template may be checked prior to any
instantiation. [ Note: Knowing which names are type names allows the
syntax of every template to be checked in this way. — end note ] The
program is ill-formed, no diagnostic required, if: [..] (8.3) every
valid specialization of a variadic template requires an empty template
parameter pack...
Source here
UPDATE
This would work too:
#include <iostream>
#include <type_traits>
void func() {
std::cout<<"End marker\n";
}
template <typename T, typename... Ts>
void func() {
std::cout << "Type sizeof " << sizeof(T) << "\n";
if constexpr (0 == sizeof...(Ts))
func();
else
func<Ts...>();
}
int main() {
func<int, int, int>();
}
Related
I am using a variadic template function where the function parameters isn't the templated types.
I got a compilation error:
Error C2668 '_TailHelper': ambiguous call to overloaded function
Here it is the code snippet.
template <typename HEAD>
void _TailHelper(int) {
std::cout << typeid(HEAD).name() << std::endl;
}
template <typename HEAD, typename ... TAILS>
void _TailHelper(int x) {
_TailHelper<HEAD>(x);
_TailHelper<TAILS...>(x);
}
int main(){
_TailHelper<int,double>(2);
}
Both overloads match with single template argument, so you have to disable one. For example like that:
#include <iostream>
#include <typeinfo>
template <typename T>
void TailHelper(int) {
std::cout << typeid(T).name() << std::endl;
}
template <typename HEAD, typename ... TAILS>
typename std::enable_if<(sizeof...(TAILS) != 0)>::type
TailHelper(int x) {
TailHelper<HEAD>(x);
TailHelper<TAILS...>(x);
}
int main() {
TailHelper<int,double>(2);
}
The ambiguous call comes from this line:
_TailHelper<HEAD>(x);
this call matches both functions the first one, and the second function which its second parameter can refer to zero or more template parameters.
As alternative to recursion, you might "loop" over your variadic:
In C++17:
template <typename... Ts>
void PrintTypes() {
((std::cout << typeid(Ts).name() << std::endl), ...);
}
In previous version, it is less elegant, and you might use some trick as:
template <typename... Ts>
void PrintTypes() {
const int dummy[] = {0, ((std::cout << typeid(Ts).name() << std::endl), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
}
Let's say I have the following function declaration:
template<typename signature>
int foo();
Given the above-mentioned function, is it possible to define foo in such a way, so that it returns the number of function arguments that were passed in the decltype template parameter?
So the example usage might look like:
int bar(int a, int b)
{
return a + b;
}
int jar(int a)
{
return a * a;
}
int main()
{
std::cout << foo<decltype(bar)>() << std::endl; // Desired output: 2
std::cout << foo<decltype(jar)>() << std::endl; // Desired output: 1
}
Edit:
Thanks, everyone for the replies. They do seem to work. However, I forgot to mention one more use case.
Let's say I want to get the number of arguments of the following function:
int __stdcall car(int a, int b, int c)
{
return a * b + c;
}
The answers so far do not seem to work with this kind of function that uses __stdcall convention.
Any idea why and what can be done about it?
For that(i.e. with decltype), the given foo is not enough. You need something like the followings traits.
template<typename> struct funtion_args final {};
template<typename ReType, typename... Args>
struct funtion_args<ReType(Args...)> final
{
static constexpr std::size_t noArgs = sizeof...(Args);
};
Used sizeof... operator on the variadic template arguments, to get the no of arguments.
And then you can get the argument count directly like
std::cout << funtion_args<decltype(bar)>::noArgs << "\n"; // output: 2
or pack into the foo
template<typename signature>
constexpr std::size_t foo() noexcept
{
return funtion_args<signature>::noArgs;
}
(See Live Demo)
Better Approach
If you want less typing(i.e. without decltype), a more convenient way of getting the arguments count of a free-function, you could do the following
template <typename ReType, typename... Args>
constexpr auto foo(ReType(*)(Args...)) noexcept
{
return sizeof...(Args);
}
Now you could conveniently call the foo with other functions as arguments
std::cout << foo(bar) << "\n"; // output: 2
(See Live Demo)
Sure, just have foo() call on a suitable trait type. For example:
template <typename T>
struct foo_helper;
template <typename T, typename... Args>
struct foo_helper<T(Args...)> {
static constexpr std::size_t arg_count = sizeof...(Args);
};
template <typename T>
std::size_t foo() {
return foo_helper<T>::arg_count;
}
I know, that there exists the possibility for automatic type deduction of function templates, given a particular function parameter, but does there also exist such a method for non type template parameters?
Example:
#include <iostream>
template<typename T, T val>
void func_a(void) {
std::cout << val << std::endl;
}
template<typename T>
void func_b(T val) {
std::cout << val << std::endl;
}
int main(void) {
func_a<uint32_t, 42u>();
//func_a<42u>(); //This line doesn't work
func_b(42u);
return 0;
}
So I don't want to give each time the template argument type uint32_t every time, when I call func_a(). Does there such a method exist in C++17 or below?
I am using g++ v.7.3 and c++17.
In C++17, you can use auto:
template<auto val>
void func_a(void) {
std::cout << val << std::endl;
}
int main(void) {
func_a<42u>();
return 0;
}
Given a +1 for the C++17 solution, a better-than-nothing C++11/C++14 solution can be the use of a macro to activate decltype() over the argument.
By example, with the macro
#define func_a_macro(val) func_a<decltype(val), val>
or better, as suggested by liliscent, to avoid problems with references
#define func_a_macro(val) \
func_a<std::remove_reference<decltype(val)>::type, val>
you can call
func_a_macro(42u)();
p.s.: I know... I know... macros are distilled evil... but sometime are useful.
C++14 solution with no macros:
template<int N> auto Int = std::integral_constant<int, N>{};
template<class T, T n>
constexpr auto foo(std::integral_constant<T, n> x)
{
std::cout << x.value << std::endl;
}
int main()
{
foo(Int<6>);
}
c++11:
template<int N> using Int = std::integral_constant<int, N>;
template<class T, T n>
constexpr void foo(std::integral_constant<T, n> x)
{
std::cout << x.value << std::endl;
}
int main()
{
foo(Int<6>());
}
I try to use std::enable_if with an unused and unnamed type parameter, in order to not distort the return type. However, the following code does not compile.
#include <iostream>
template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
The compiler says:
7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:
What is the problem here? How do I have to change the code to get it compile? The text book "Discovering Modern C++" explicitly encourages the use of std::enable_if with anonymous type parameters.
EDIT: I know that it works if I put std::enable_if into the return type. However, my intention is to get some more details why it does not work if I use it with anonymous type parameters. As I said, my text book encourages the variant using anonymous type parameters, so I am wondering why my code does not compile.
However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.
Default values do not participate in overload resolution, thus you are actually redefining the same function.
Let's simplify your example:
template<typename = int>
void f() {}
template<typename = void>
void f() {}
int main() {
f<>();
}
The code above does not compile, for it couldn't know what version of f you want to invoke.
In your case, if I invoke foo as foo<void, void>, I've almost the same problem.
The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.
Because of that, the code is ill-formed and the compiler correctly gives you an error.
As a side note, you can still have it working without using the std::enable_if_t in the return type.
As an example:
#include <type_traits>
#include <iostream>
template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, #T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:
It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.
You can put enable_if into the return type:
template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }
By the way, enable_if_t is available from C++14, so you might want to say typename std::enable_if<std::is_integral<T>::value, T>::type instead. Quite a mouthful.
But a little more idiomatic (and readable) would be to dispatch basing on the type:
template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }
template <typename T>
T foo(){
return foo_impl<T>(typename std::is_integral<T>::type{});
}
There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.
template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }
Your error is that you're using enable_if_t on the right of the equal sign.
You have to use it on the left
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
But this work with C++14.
In C++11 (your question is tagged C++11) you don't have enable_if_t.
The code become
#include <iostream>
#include <type_traits>
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
In the below code snippet,
template<typename T1>
void func(T1& t)
{
cout << "all" << endl;
}
template<typename T2>
void func(T2 &t)
{
cout << "float" << endl;
}
// I do not want this
// template<> void func(float &t)
int main()
{
int i; float f;
func(i); // should print "all"
func(f); // should print "float"
return 0;
}
I would like to have the templates modified which by passing any type other than float will print "all" and passing float will print "float". I do not want template specialization, instead have partial specialization which will act accordingly based on input type. How should i go about it. Thanks in advance.
Well the scenario, i'm currently facing is like,
I need to have the following defined,
template<typename T1>
void func(T1 &t)
{
cout << "t1" << endl;
}
template<typename T2>
void func(T2 &t)
{
cout << "t2" << endl;
}
The following calls should print "t2"
func(int) // print "t2"
func(float) // print "t2"
func(string) // print "t2"
The following calls should print "t1"
func(char) // print "t1"
func(xyz) // print "t1"
...
func(abc) // print "t1"
some kind of grouping like the above where few should call the partial specialization implementation and others should call the default implementation.
You can combine function overloading with templates. So:
#include <iostream>
template<typename T>
void func(T& t)
{
std::cout << "all" << std::endl;
}
void func(float& f)
{
std::cout << "float" << std::endl;
}
int main()
{
int i; float f;
func(i); // prints "all"
func(f); // prints "float"
return 0;
}
Write a type traits class for your condition:
template<class T>
struct IsIntFloatOrString {
enum { value = boost::is_same<T, int>::value
or boost::is_same<T, float>::value
or boost::is_same<T, string>::value };
};
Use boost::enable_if and disable_if:
template<typename T1>
typename boost::enable_if<IsIntFloatOrString<T1> >::type
func(T1 &t) {
cout << "t1" << endl;
}
template<typename T2>
typename boost::disable_if<IsIntFloatOrString<T2> >::type
func(T2 &t) {
cout << "t2" << endl;
}
You cannot partially specialise functions in C++.
Perhaps this is not the terminology you mean. You can use templates like boost::is_same<T1, T2> to perform conditional logic based on the given template parameter. You can also use T in any place where you'd use any other type, such as in typeid(T).name():
template <typename T>
void foo(T&) {
if (boost::is_same<T, int>::value)
std::cout << "int lol";
else
std::cout << typeid(T).name();
}
(Although I'd not recommend using typeid().name() as its value is not specified by the standard and can vary from the type written in your code, to a mangled symbol, or the lyrics to Pokerface.)
Addendum Like other answerers, I would personally choose template specialisation itself or just plain ol' function overloading. I don't know why you're averse to them, but that is what they are there for.
As Tomalak already said in his answer you can not partially specialize a template function, but if you change your function to be a static member function in a template class, you could do it.
However, a better approach would be function overloading.
This is how to make it work without ugly syntax a and !b and !c for enable_if in case of arbitrary number of conditions.
If we know that partial specialization don't work work function but work with classes, let's use classes! We should hide it from people, but we can use them!
OK, code:
#include <type_traits>
#include <iostream>
template <typename T>
class is_int_or_float : public std::integral_constant<bool, std::is_same<T, int>::value || std::is_same<T, float>::value> {
};
template<typename T, typename Enable = void> //(2)
struct Helper {
static void go(const T&) {
std::cout << "all"<< std::endl;
}
};
template<typename T>
struct Helper<T, typename std::enable_if<is_int_or_float<T>::value>::type> { // (3)
static void go(const T&) {
std::cout << "int or float" << std::endl;
}
};
template<typename T>
struct Helper<T, typename std::enable_if<std::is_pointer<T>::value>::type> { // (3)
static void go(const T&) {
std::cout << "pointer" << std::endl;
}
};
template<typename T>
void func(const T& arg) {
Helper<T>::go(arg); // (1)
}
int main() {
char c;
int i;
float f;
int* p;
func(c);
func(i);
func(f);
func(p);
}
(1) First of all just for every type call helper. No specialization for functions.
(2) Here we add one dummy argument. We don't have to specify it on calling because it's default to void
(3) In 3 we just give void, when we allow T and anything else (or SFINAE as in our case). One important thing is that we shouldn't allow some T twice or more.
Notes:
We can also change default type to std::true_type, after that we will be able to get rid of std::enable_if (std::enable_if<some_trait<T>::value> will be change to just some_trait<T>::type). I'm not sure which
This code uses type traits from C++11. If you don't have c++11 support you may write your own traits or use type traits from boost
Live example