Here an example:
#include <iostream>
template<typename T,
typename ... Args>
void print(T&& t, Args&& ... args)
{
// line where compilation fails when the A::run is called
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
{
t.display(std::forward<Args>(args)...);
}
else
{
std::cout << "not applicable !" << std::endl;
}
}
template<typename T>
class A
{
public:
A(T&& t):t_(t){}
template <typename... Args>
void run(Args&& ... args)
{
print<T,Args...>(t_,std::forward<Args>(args)...);
}
T t_;
};
template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;
class B
{
public:
B(int value):value_(value){}
void display(int a, int b)
{
std::cout << value_ << " "
<< a << " "
<< b << std::endl;
}
int value_;
};
int main()
{
int v1=10;
int v2=20;
B b1{1};
A a1{b1};
a1.t_.display(v1,v2);
A a2{B(2)};
a2.t_.display(v1,v2);
//a1.run(v1,v2); // (1)
//a2.run(v1,v2); // (2)
//a1.run(v1);
return 0;
}
The code above compiles and runs fine. But if the 3 last lines (calls to run()
) are un-commented, the following compilation error occurs:
(1)
main.cpp:7:48: error: ‘display’ is not a member of ‘B&’
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
(2)
main.cpp:27:25: error: no matching function for call to ‘print(B&, int&, int&)’
print<T,Args...>(t_,std::forward<Args>(args)...);
Note :
template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;
explained here:
c++ copy (reference) constructor and move constructor of class cohabitation
Problem (1) and (2) are different problems.
Problem (1) come from the fact that in the following std::is_invocable_v
template<typename T,
typename ... Args>
void print(T&& t, Args&& ... args)
{
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
// no T but std::decay_t<T> ...............^...........^
instead of T
(that can be a reference) you need the "decayed" type
I propose
template <typename T, typename ... Args>
void print (T && t, Args && ... args)
{
using U = std::decay_t<T>;
if constexpr ( std::is_invocable_v<decltype(&U::display), U*, Args...> )
t.display(std::forward<Args>(args)...);
else
std::cout << "not applicable !" << std::endl;
}
Problem (2) come from the fact that explicating the template parameters in the following print()
call
template <typename... Args>
void run(Args&& ... args)
{
print<T,Args...>(t_,std::forward<Args>(args)...);
}
you impede the correct template deduction.
Suggestion: let template deduction, and perfect forwarding, works and call the function as follows
print(t_,std::forward<Args>(args)...);