Template specialization based on arguments of template parameter member function [duplicate] - c++

This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 6 years ago.
I can run this code, however when I enable the 3 out-commented lines it does not compile anymore and gives the following error:
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(41): warning C4346: 'first_argument<F>::type': dependent name is not a type
1> d:\git\testprojekt\testprojekt\testprojekt.cpp(41): note: prefix with 'typename' to indicate a type
1> d:\git\testprojekt\testprojekt\testprojekt.cpp(43): note: see reference to class template instantiation 'Bla<Type>' being compiled
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(41): error C2923: 'DoStuff': 'first_argument<F>::type' is not a valid template type argument for parameter 'Arg'
1> d:\git\testprojekt\testprojekt\testprojekt.cpp(22): note: see declaration of 'first_argument<F>::type'
My idea why it isnt working is that the compiler wants to make sure Bla compiles for all sorts of template parameters, but first_argument can only handle template parameters that have the operator() defined.
Does anybody know how to make this example work?
I need it to select a class, here doStuff, based on whether the template parameters operator() accepts an argument or not, inside another templated class, here Bla.
#include <iostream>
template<typename F, typename Ret>
void helper(Ret(F::*)());
template<typename F, typename Ret>
void helper(Ret(F::*)() const);
template<typename F, typename Ret, typename A, typename... Rest>
char helper(Ret(F::*)(A, Rest...));
template<typename F, typename Ret, typename A, typename... Rest>
char helper(Ret(F::*)(A, Rest...) const);
template<typename F>
struct first_argument {
typedef decltype(helper(&F::operator())) type;
};
template <typename Functor, typename Arg = first_argument<Functor>::type>
struct DoStuff;
template <typename Functor>
struct DoStuff<Functor, char>
{
void print() { std::cout << "has arg" << std::endl; };
};
template <typename Functor>
struct DoStuff<Functor, void>
{
void print() { std::cout << "does not have arg" << std::endl; };
};
template <typename Type>
struct Bla
{
//DoStuff<typename Type> doStuff;
//void print() { doStuff.print(); };
};
int main()
{
struct functorNoArg {
void operator() () {};
};
struct functorArg {
void operator()(int a) { std::cout << a; };
};
auto lambdaNoArg = []() {};
auto lambdaArg = [](int a) {};
std::cout << std::is_same<first_argument<functorArg>::type,int>::value <<std::endl; // this works
DoStuff<functorArg> doStuff;
doStuff.print();
DoStuff<functorNoArg> doStuff2;
doStuff2.print();
DoStuff<decltype(lambdaArg)> doStuff3;
doStuff3.print();
DoStuff<decltype(lambdaNoArg)> doStuff4;
doStuff4.print();
Bla<functorArg> bla;
//bla.print();
return 0;
}
Thanks to all template nerds for helping :)

In your struct Bla you should say DoStuff<Type> doStuff; (typename is not needed or allowed here).
In (corrected version):
template <typename Functor, typename Arg = typename first_argument<Functor>::type>
struct DoStuff;
You were missing typename before first_argument<Functor>::type.

Related

Can I use enable_if to have two functions with same name, same arguments but different return type? [duplicate]

I already used the SFINAE idiom quite a few times and I got used to put my std::enable_if<> in template parameters rather than in return types. However, I came across some trivial case where it didn't work, and I'm not sure why. First of all, here is my main:
int main()
{
foo(5);
foo(3.4);
}
Here is an implementation of foo that triggers the error:
template<typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
And here is a supposedly equivalent piece of code that works fine:
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_integral<T>::value>::type
{
std::cout << "I'm an integrer!\n";
}
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_floating_point<T>::value>::type
{
std::cout << "I'm a floating point number!\n";
}
My question is: why does the first implementation of foo triggers that error while the second one does not trigger it?
main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)'
auto foo(T)
^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here
auto foo(T)
^
main.cpp: In function 'int main()':
main.cpp:23:12: error: no matching function for call to 'foo(double)'
foo(3.4);
^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T)
auto foo(T)
^
main.cpp:6:6: note: template argument deduction/substitution failed:
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>'
typename = typename std::enable_if<std::is_integral<T>::value>::type>
^
EDIT :
Working code and faulty code.
You should take a look at 14.5.6.1 Function template overloading (C++11 standard) where function templates equivalency is defined. In short, default template arguments are not considered, so in the 1st case you have the same function template defined twice. In the 2nd case you have expression referring template parameters used in the return type (again see 14.5.6.1/4). Since this expression is part of signature you get two different function template declarations and thus SFINAE get a chance to work.
Values in the templates work:
template<typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
The = ... of the template just gives a default parameter. This is not part of the actual signature which looks like
template<typename T, typename>
auto foo(T a);
for both functions.
Depending on your needs, the most generic solution for this problem is using tag dispatching.
struct integral_tag { typedef integral_tag category; };
struct floating_tag { typedef floating_tag category; };
template <typename T> struct foo_tag
: std::conditional<std::is_integral<T>::value, integral_tag,
typename std::conditional<std::is_floating_point<T>::value, floating_tag,
std::false_type>::type>::type {};
template<typename T>
T foo_impl(T a, integral_tag) { return a; }
template<typename T>
T foo_impl(T a, floating_tag) { return a; }
template <typename T>
T foo(T a)
{
static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value,
"T must be either floating point or integral");
return foo_impl(a, typename foo_tag<T>::category{});
}
struct bigint {};
template<> struct foo_tag<bigint> : integral_tag {};
int main()
{
//foo("x"); // produces a nice error message
foo(1);
foo(1.5);
foo(bigint{});
}

Hide class template instance based on traits

I have a traits class like the following that reflects the compatibility between two types:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
The single member determines if SpecialMethod() can be called on objects of type ObjectType with argument of type ArgumentType.
A simple class that supports this is the following:
class ClassWithSpecialMethod
{
public:
template <typename T>
void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};
template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
static const bool SpecialMethodAvailable = true;
};
I want to write a worker class that uses this traits class and calls the special method if it is available. Basically something like the following:
template <typename T>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
//if Traits<GlobalDataType, T>::SpecialMethodAvailable
// call the method
//else
// do something different
}
};
I tried to realize this using std::enable_if. My solution works with the Visual C 14.1 compiler but not with GCC. Here is what I tried:
template <typename T, typename Enable = void>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
std::cout << "There is no special method (called with " << t << ")" << std::endl;
}
};
template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
};
I used this as follows:
typedef ... GlobalDataType; //before the template declarations
int main()
{
GlobalDataType td;
int integer = 0;
Worker<int>::DoSomething(integer, td);
}
If GlobalDataType is typedef'ed to ClassWithSpecialMethod, both VS and GCC compile fine and output correctly:
Special Method called with 0
However, if GlobalDataType is typedef'ed to something that does not allow the special method (e.g. int), VS still produces the correct output while GCC results in a compile error:
In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’:
source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type
GlobalDataType {aka int}’
Can someone explain why this does not work as intended under GCC? What would be alternatives?
Link to online compiler
As explained by Jarod42, this method
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
with GlobalDataType fixed as int, is ever wrong (for ever T type) because it's sure that int is without SpecialMethod().
To solve this problem with a minimum code change, you can templatize the second parameter
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
If you want that DoSomething() receive (as second parameter) only a GlobalDataType, you can impose it enabling DoSomething using SFINAE, only if U is GlobalDataType. Something as
template <typename U>
static typename std::enable_if<std::is_same<U, GlobalDataType>{}>
DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
What would be alternatives?
I propose you a completely different way, based (following the std::declval() example) over declaration of functions.
First of all, a couple of template helper functions
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethodHelper (int)
-> decltype(std::declval<ObjectType>.SpecialMethod(std::declval<Args>...),
std::true_type{} );
template <typename ... Args>
constexpr std::false_type withSpecialMethodHelper (long);
Now you can write the declaration of a template function that return std::true_type if ObjectType has a SpecialMethod() that can be called with a variadic list of arguments of type Args...
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
-> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
or maybe better, as suggested by Jarod42, through using
template <typename ObjectType, typename ... Args>
using withSpecialMethod
= decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
If you can use C++14, you can also define a withSpecialMethod_v template constexpr variable
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= decltype(withSpecialMethod<ObjectType, Args...>())::value;
in case of declared function or
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= withSpecialMethod<ObjectType, Args...>::value;
in case of using, that can simplify the use.
Now the Worker class and specialization become
template <typename T, bool = withSpecialMethod_v<GlobalDataType, T>>
struct Worker
{
static void DoSomething (T t, GlobalDataType & globalData)
{
std::cout << "There is no special method (called with " << t << ")"
<< std::endl;
}
};
template <typename T>
struct Worker<T, true>
{
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
};
Mvsc 14 doesn't do the 2 phases look-up needed for template.
gcc does (and is correct).
globalData.SpecialMethod(t); is incorrect for any t immediatly so the error. (globalData.SpecialMethod is incorrect and doesn't depend of template parameter).
By post-pone the evaluation you might have what you want:
template <typename T>
struct Worker<T, std::enable_if_t<Traits<GlobalDataType, T>::SpecialMethodAvailable>>
{
template <typename G, typename U>
static void f(G& g, U& u)
{
g.SpecialMethod(u);
}
static void DoSomething(T t, GlobalDataType& globalData)
{
f(globalData, t);
}
};
Demo

Function template taking a template non-type template parameter

How does one take a templated pointer to a member function?
By templated I mean that the following types are not known in advance:
template param T is class of the pointer to member
template param R is the return type
variadic template param Args... are the parameters
Non-working code to illustrate the issue:
template <???>
void pmf_tparam() {}
// this works, but it's a function parameter, not a template parameter
template <class T, typename R, typename... Args>
void pmf_param(R (T::*pmf)(Args...)) {}
struct A {
void f(int) {}
};
int main() {
pmf_tparam<&A::f>(); // What I'm looking for
pmf_param(&A::f); // This works but that's not what I'm looking for
return 0;
}
Is it possible to achieve the desired behavior in C++11?
I don't think this notation is possible, yet. There is proposal P0127R1 to make this notation possible. The template would be declared something like this:
template <auto P> void pmf_tparam();
// ...
pmf_tparam<&S::member>();
pmf_tparam<&f>();
The proposal to add auto for non-type type parameters was voted into the C++ working paper in Oulu and the result was voted to become the CD leading towards C++17 also in Oulu. Without the auto type for the non-type parameter, you'd need to provide the type of the pointer:
template <typename T, T P> void pmf_tparam();
// ...
pmf_tparam<decltype(&S::member), &S::member>();
pmf_tparam<decltype(&f), &f>();
As you've not said really what you are after in the function, the simplest is:
struct A {
void bar() {
}
};
template <typename T>
void foo() {
// Here T is void (A::*)()
}
int main(void) {
foo<decltype(&A::bar)>();
}
However if you want the signature broken down, I'm not sure there is a way to resolve the types directly, however you can with a little indirection...
struct A {
void bar() {
std::cout << "Call A" << std::endl;
}
};
template <typename R, typename C, typename... Args>
struct composer {
using return_type = R;
using class_type = C;
using args_seq = std::tuple<Args...>;
using pf = R (C::*)(Args...);
};
template <typename C, typename C::pf M>
struct foo {
static_assert(std::is_same<C, composer<void, A>>::value, "not fp");
typename C::return_type call(typename C::class_type& inst) {
return (inst.*M)();
}
template <typename... Args>
typename C::return_type call(typename C::class_type& inst, Args&&... args) {
return (inst.*M)(std::forward<Args...>(args...));
}
};
template <class T, typename R, typename... Args>
constexpr auto compute(R (T::*pmf)(Args...)) {
return composer<R, T, Args...>{};
}
int main() {
foo<decltype(compute(&A::bar)), &A::bar> f;
A a;
f.call(a);
}
The above should do what you are after...
What you can do is
template <template T, T value>
void pmf_tparam() {}
and then
pmf_tparam<decltype(&A::f), &A::f>();
The problem is not knowing the type of the argument and wanting a template argument of that type.
With an additional decltype (still in the templated parameter), this works:
#include <iostream>
using namespace std;
template <typename T, T ptr>
void foo (){
ptr();
}
void noop() {
cout << "Hello" << endl;
}
int main() {
//Here have to use decltype first
foo<decltype(&noop), noop>();
return 0;
}

Getting the argument type of a function with template argument

I have a code that works, except the following line
std::is_same<first_argument<functorArgTemplated>::type,int>::value <<std::endl; // this does not work
Does anybody know what kind of definitions of helper() I have to add?
The compiler error is
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(60): error C2065: 'functorArgTemplated': undeclared identifier
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(60): error C2923: 'first_argument': 'functorArgTemplated' is not a valid template type argument for parameter 'F'
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(60): error C2955: 'first_argument': use of class template requires template argument list
1> d:\git\testprojekt\testprojekt\testprojekt.cpp(22): note: see declaration of 'first_argument'
1>d:\git\testprojekt\testprojekt\testprojekt.cpp(60): warning C4552: '<<': operator has no effect; expected operator with side-effect
complete code:
#include <iostream>
template<typename F, typename Ret>
void helper(Ret(F::*)());
template<typename F, typename Ret>
void helper(Ret(F::*)() const);
template<typename F, typename Ret, typename A, typename... Rest>
void helper(Ret(F::*)(A, Rest...));
template<typename F, typename Ret, typename A, typename... Rest>
void helper(Ret(F::*)(A, Rest...) const);
template<typename F, typename Ret, typename A, typename... Rest>
void helper(Ret(F::*)(A, Rest...) const);
template<typename F>
struct first_argument {
typedef decltype(helper(&F::operator())) type;
};
template <typename Functor, typename Arg = typename first_argument<Functor>::type>
struct DoStuff;
template <typename Functor>
struct DoStuff<Functor, char>
{
void print() { std::cout << "has arg" << std::endl; };
};
template <typename Functor>
struct DoStuff<Functor, void>
{
void print() { std::cout << "does not have arg" << std::endl; };
};
struct functorNoArg {
void operator() () {};
};
struct functorArg {
void operator()(int a) { std::cout << a; };
};
struct functorArgTemplated {
template <typename TArg>
int operator()(TArg a) { std::cout << a; return a; };
};
int main()
{
auto lambdaNoArg = []() {};
auto lambdaArg = [](int a) {};
std::cout << std::is_same<first_argument<functorArgTemplated>::type,int>::value <<std::endl; // this does not work
DoStuff<functorArg> doStuff;
doStuff.print();
DoStuff<functorNoArg> doStuff2;
doStuff2.print();
DoStuff<decltype(lambdaArg)> doStuff3;
doStuff3.print();
DoStuff<decltype(lambdaNoArg)> doStuff4;
doStuff4.print();
return 0;
}
In C++ you can answer the question "can I invoke this with an int" and get an answer (in the immediate context), but you cannot answer "what can I invoke this with".
In the general case answering the second question requires solving Halt, as what makes an acceptable argument to an object in C++ can be a Turing Complete computation.
You solved the problem for simple cases: the problem in general cannot be solved. Solving it beyond the simplest of cases requires increasing amounts of hacks that get the answer wrong in various ways.
Usually trying to answer the question is a mixture of asking the wrong question or wanting to DRY when writing scripting/language interface code (often with languages with less ridiculous metaprogramming or type system, or an interface that narrows the type information that is transmitted). People asking this question on SO tend to ask about their narrow issue without bothering to mention what led them down this dead end path: it is always a good idea to post a MCVE and a sketch of the motivating reason behind the problem.

C++ std::function cannot find correct overload

Consider the following case:
void Set(const std::function<void(int)> &fn);
void Set(const std::function<void(int, int)> &fn);
Now calling the function with
Set([](int a) {
//...
});
Gives "ambiguous call to overloaded function" error. I am using Visual Studio 2010. Is there a work around or another method to achieve something similar. I cannot use templates, because these functions are stored for later use because I cannot determine the number of parameters in that case. If you ask I can submit more details.
I would suggest this solution. It should work with lambdas as well as with function-objects. It can be extended to make it work for function pointer as well (just go through the link provided at the bottom)
Framework:
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
};
template<typename Functor, size_t NArgs>
struct count_arg : std::enable_if<function_traits<Functor>::arity==NArgs, int>
{};
Usage:
template<typename Functor>
typename count_arg<Functor, 1>::type Set(Functor f)
{
std::function<void(int)> fn = f;
std::cout << "f with one argument" << std::endl;
}
template<typename Functor>
typename count_arg<Functor, 2>::type Set(Functor f)
{
std::function<void(int, int)> fn = f;
std::cout << "f with two arguments" << std::endl;
}
int main() {
Set([](int a){});
Set([](int a, int b){});
return 0;
}
Output:
f with one argument
f with two arguments
I took some help from the accepted answer of this topic:
Is it possible to figure out the parameter type and return type of a lambda?
Work around for Visual Studio 2010
Since Microsoft Visual Studio 2010 doesn't support variadic templates, then the framework-part can be implemented as:
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename C, typename R, typename T0>
struct function_traits<R(C::*)(T0) const> { enum { arity = 1 }; };
template <typename C, typename R, typename T0, typename T1>
struct function_traits<R(C::*)(T0,T1) const> { enum { arity = 2 }; };
template <typename C, typename R, typename T0, typename T1, typename T2>
struct function_traits<R(C::*)(T0,T1,T2) const> { enum { arity = 3 }; };
//this is same as before
template<typename Functor, size_t NArgs, typename ReturnType=void>
struct count_arg : std::enable_if<function_traits<Functor>::arity==NArgs, ReturnType>
{};
EDIT
Now this code supports any return type.
I suggest:
void Set(void(*f)(int, int))
{
std::function<void(int,int)> wrap(f);
// ...
}
void Set(void(*f)(int))
{
std::function<void(int)> wrap(f);
// ...
}
You can manually specify the type:
Set(std::function<void(int)>([](int a) {
//...
}));