Is there a conceptual (or other) difference between specializing function template and using if constexpr?
E.g.:
template<class T>
void fun(T t)
{
// Generic code.
}
template<>
void fun(int t)
{
// int code.
}
Vs.
template<class T>
void fun(T t)
{
if constexpr(std::is_same_v<T, int>)
{
// int code.
}
else
{
// Generic code.
}
}
Related
This question already has answers here:
Template Function Specialization for Integer Types
(6 answers)
Closed last year.
#include <iostream>
#include <type_traits>
#include <math.h>
template<typename T>
void Foo(T x)
{
if(std::is_integral<T>::value)
{
rint(x);
}
else
{
std::cout<<"not integral"<<std::endl;
}
}
int main()
{
Foo("foo");
return 0;
}
Does not compile because there is no suitable rint overload for const char*, but rint will never be invoked with const char* because it's not an integral type. How can I inform the compiler about this
One way would be to use std::enable_if as shown below:
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type Foo(T x)
{
std::cout<<"Foo called for integral type"<<std::endl;
rint(x);
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type Foo(T x)
{
std::cout<<"non-integral type"<<std::endl;
}
int main()
{
Foo(5);// calls Foo integral type
Foo('c'); //calls Foo for integral type
Foo("c"); //call Foo for non-integral type
return 0;
}
In c++17 you could use if constexpr instead of if and it would work.
In c++14 you could specialize a struct to do what you want
template<class T, bool IsIntegral = std::is_integral<T>::value>
struct do_action
{
void operator()(T val) { rint(val); }
};
template<class T>
struct do_action<T, false>
{
void operator()(T val) { std::cout << "Not integral\n"; }
};
template<class T>
void Foo(T x)
{
do_action<T>()(x);
}
In C++20, we can now use concepts instead of SFINAE to figure out whether a function exists in a template typename:
template<typename T> concept fooable = requires (T a) {
a.foo();
};
class Foo {
public:
// If commented out, will fail compilation.
void foo() {}
void bar() {}
};
template <typename T> requires fooable<T>
void foo_it(T t) {
t.bar();
}
int main()
{
foo_it(Foo());
}
How do we do this with functions that have non-empty arguments?
You might have extra parameters in requires:
template<typename T> concept fooable = requires (T a, int i) {
a.foo(i);
};
Demo
The best option seems to be declval:
template<typename T> concept fooable = requires (T a) {
a.foo(std::declval<int>());
};
class Foo {
public:
void foo(int x) {}
void bar() {}
};
template <typename T> requires fooable<T>
void foo_it(T t) {
t.bar();
}
int main()
{
foo_it(Foo());
}
Given a function that calls a templated function argument and calls another function that does something with the returned value:
template <typename T>
void doSomething(T &&) {
// ...
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething(func());
}
How can this be extended to work with a functor that returns void? Ideally, I would like to add an overloaded void doSomething() { ... } that is called if func's return type is void.
Currently this just results in an error: invalid use of void expression if func returns void.
Working example on Ideone
I think you could create a struct helper to use overloaded , operator more or less like this:
#include <type_traits>
#include <utility>
struct my_void { };
struct my_type { };
template <class T, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
my_void operator,(T, my_type) { return {}; }
template <class T, typename std::enable_if<!std::is_void<T>::value>::type* = nullptr>
T &&operator,(T &&val, my_type) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething((func(), my_type{}));
}
int main() {
auto func1 = []() -> bool { return true; };
auto func2 = []() -> void { };
call(func1);
call(func2);
}
[live demo]
Edit:
Thanks to Piotr Skotnicki and Holt (they pointed out that the first overload actually wouldn't ever be triggered and proposed simplified version of the approach):
#include <type_traits>
#include <utility>
struct dumb_t { };
template <class T>
T &&operator,(T &&val, dumb_t) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething((func(), dumb_t{}));
}
int main() {
auto func1 = []() -> bool { return true; };
auto func2 = []() -> void { };
call(func1);
call(func2);
}
[live demo]
doSomething() takes a parameter, and a parameter cannot be void.
So, in order for this to work, you also need an overloaded doSomething() that takes no parameters. This is going to be the first step:
template <typename T>
void doSomething(T &&) {
// ...
}
void doSomething()
{
}
So, you're going to have to do this first, before you can even get off the ground.
It's also possible that you would like to supply a default value for the parameter, in case the functor returns a void; and still use a single template. That's another possibility, and the following solution can be easily adjusted, in an obvious way, to handle that.
What needs to happen here is a specialization of call() for a functor that returns a void. Unfortunately, functions cannot be partially specialized, so a helper class is needed:
#include <utility>
template <typename T>
void doSomething(T &&) {
// ...
}
void doSomething()
{
}
// Helper class, default implementation, functor returns a non-void value.
template<typename return_type>
class call_do_something {
public:
template<typename functor>
static void call(functor &&f)
{
doSomething(f());
}
};
// Specialization for a functor that returns a void.
//
// Trivially changed to call the template function instead, with
// a default parameter.
template<>
class call_do_something<void> {
public:
template<typename functor>
static void call(functor &&f)
{
f();
doSomething();
}
};
// The original call() function is just a wrapper, that selects
// the default or the specialized helper class.
template <typename T_Func>
void call(T_Func &&func) {
call_do_something<decltype(func())>::call(std::forward<T_Func>(func));
}
// Example:
void foobar()
{
call([] { return 1; });
call([] {});
}
You can provide an extra pair of overloaded helper functions (named callDispatch in my example below) to dispatch the call as required, which eliminates the need for partial specialisation and thus helper classes. They are overloaded by using different signature specifications for the std::function objects they take (live code here):
#include <iostream>
#include <functional>
int func1()
{
return 1;
}
void func2()
{
}
template <typename T>
void doSomething(T &&)
{
std::cout << "In param version" << std::endl;
// ...
}
void doSomething()
{
std::cout << "In no-param version" << std::endl;
// ...
}
template <typename R, typename ... Args>
void callDispatch(std::function<R(Args...)> &&f)
{
doSomething(f());
}
template <typename ... Args>
void callDispatch(std::function<void(Args...)> &&f)
{
f();
doSomething();
}
template <typename T>
void call(T &&func) {
callDispatch(std::function<std::remove_reference_t<T>>(func));
}
int main() {
call(func1);
call(func2);
}
One lean variant would be to give the method a function as parameter. Then you evaluate the expression inside the method and see if did anything. In general this is usally bad practice, since you usually can infer how it returns stuff and when it is needed.
C++ Primer 5th Edition has a snippet of advice at the end of chapter 16.3 (a chapter discussing function template overloading):
Declare every function in an overload set before you define any of the
functions. That way you don’t have to worry whether the compiler will
instantiate a call before it sees the function you intended to call.
So is this telling me that in choosing the candidate and viable functions during overload resolution it is possible the compiler might instantiate a function template that isn't chosen in the end? I tried to see whether this might actually happen:
template<class> struct always_false : std::false_type {};
template <typename T> void test(T const &){
static_assert(always_false<T>::value, "If this fires, it is instantiated");
}
template <typename T> void test(T*) { }
int main(){
int *q = nullptr;
test(q); //test(T*) should be the best match
}
This program would throw a compiler error if test(T const &) was instantiated in any form, except the program compiles fine as expected. So what kind of compilation mishap is that tip trying to guard me from? When would it ever instantiate a function before it saw the function I was trying to call?
The author is warning you of this:
template<class> struct always_false : std::false_type {};
template <typename T> void test(T const &){
static_assert(always_false<T>::value, "If this fires, it is instantiated");
}
int main(){
int *q = nullptr;
test(q); //test(T*) will not be matched.
}
template <typename T> void test(T*)
{
}
And these:
template<class> struct always_false : std::false_type {};
template <typename T> void test(T const &){
static_assert(always_false<T>::value, "If this fires, it is instantiated");
}
template <> void test<int>(int const &);
void test(int *);
int main(){
int *q = nullptr;
test(q); //test(int*) should be the best match
int a;
test(a); // test<int>(int const&) should be the best match
}
template <> void test<int>(int const &)
{
}
void test(int *)
{
}
If you don't provide declarations of
template <> void test<int>(int const &);
void test(int *);
before main, they won't be matched in main.
I've seen plenty of SO questions that is some variation of
template<class T, class... Ts>
T sum(T t, Ts... ts) { return t + sum(ts...); }
// ^ |
// |--------------------------------
// only one visible in
// definition context
template<class T>
T sum(T t) { return t; }
int main() {
sum(1, 2); // doesn't compile
}
(The return type isn't perfect, but you get the idea.)
And then people are surprised when it doesn't compile.
Or, even more fun,
template<class T> void f(T t) { f((int)t); }
void f(int) { /*...*/ }
int main() {
f(1L); // infinite recursion
}
I have this hpp
namespace A
{
template<class T>
class MyC
{
public:
T a;
};
template<class T>
void F(T r);
}
and this cpp
template<>
void A::F<double>(double r)
{
r;
}
template<>
void A::F<int>(int r)
{
r;
}
template<class T>
void A::F<A::MyC<T>>(A::MyC<T> r)
{
r;
}
template void A::F<A::MyC<int>>(A::MyC<int>);
template void A::F<A::MyC<double>>(A::MyC<double>);
but compiler says "unable to match function definition to an existing declaration" about F.
What's wrong with this code?
Put those declarations all in namespace A { ... } and remove A::. On the other hand, function template partial specialization is not allowed and this will make error:
template<class T>
void F<MyC<T>>(MyC<T> r)
{
...
}