In C++11, I implement function template specialization for identifying inheritance, but it occurred compile-time errors.
f() checks whether the specified class is derived from Base or not.
Following is a source code.
#include <iostream>
#include <type_traits>
using namespace std;
struct Base {};
struct Derived : Base {};
struct Base2 {};
template<typename T, bool = std::is_base_of<Base, T>::value>
void f() {
cout << "T is not Base or Base-derived class." << endl;
};
template<typename T>
void f<T, true>() {
cout << "T is Base or Base-derived class." << endl;
};
int main() {
f<Base>(); // ok
f<Derived>(); // ok
f<Base2>(); // not ok
return 0;
}
Following is error messages.
prog.cpp:15:17: error: non-class, non-variable partial specialization 'f<T, true>' is not allowed
void f<T, true>() {
^
prog.cpp: In function 'int main()':
prog.cpp:20:13: error: call of overloaded 'f()' is ambiguous
f<Base>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Base; bool <anonymous> = true]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Base]
void f<T, true>() {
^
prog.cpp:21:16: error: call of overloaded 'f()' is ambiguous
f<Derived>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Derived; bool <anonymous> = true]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Derived]
void f<T, true>() {
^
prog.cpp:22:14: error: call of overloaded 'f()' is ambiguous
f<Base2>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Base2; bool <anonymous> = false]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Base2]
void f<T, true>() {
^
How can I solve it?
When std::is_base_of<Base, T>::value evaluates true you have two functions with same signature. Therefore you get error "call ... is amibguous".
Try simple overloading as one of the solutions:
namespace detail {
void doIt(std::false_type) {
cout << "T is not Base or Base-derived class." << endl;
};
void doIt(std::true_type) {
cout << "T is Base or Base-derived class." << endl;
};
}
template<typename T>
void f() {
detail::doIt(typename std::is_base_of<Base, T>::type());
};
Of course the function detail::doIt() can be more complex and templated by T.
EDIT: add "detail::" into a function f() call.
Related
I have the following c++ code
#include <iostream>
template<typename Func>
class Foo
{
private:
Func func;
public:
Foo(Func func) : func(func) {}
template<typename T>
Func wrap()
{
Func clbk = func;
auto wrapperCB = [clbk](T t) {
auto job = [clbk, t](){
clbk(t);
};
job();
};
return wrapperCB;
}
template<typename T>
void call(T t)
{
func(t);
}
};
int main()
{
int m = 2;
auto f = [](int & p) {std::cout << "test success " << p << "\n";};
auto obj = std::make_shared<Foo<std::function<void(int &)>>>(f);
auto wrapper = obj->template wrap<int &>();
wrapper(m);
return 0;
}
This is giving compilation error
tsavs-mbp:p utsagarw$ clear; g++ -std=c++11 a.cpp -o z; ./z
a.cpp:18:17: error: no matching function for call to object of type 'const std::__1::function<void (int &)>'
clbk(t);
^~~~
a.cpp:38:32: note: in instantiation of function template specialization 'Foo<std::__1::function<void (int &)> >::wrap<int &>' requested here
auto wrapper = obj->template wrap<int &>();
^
/Applications/Xcode_10.1/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1677:9: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
_Rp operator()(_ArgTypes...) const;
^
1 error generated.
I don't understand this error. Where did this const come from?
It is building successfully if in wrap I don't create job functor and call clbk directly. What is this job doing to type T?
template<typename T>
Func wrap()
{
Func clbk = func;
auto wrapperCB = [clbk](T t) {
clbk(t);
};
return wrapperCB;
}
If you want to modify any captured variable inside lambda you have to specify it as mutable.
t variable is captured by copy, so you can only read it:
auto job = [clbk, t]() // <-- t passed by copy
{
clbk(t); // clbk takes t by reference -> int&
};
your callback, clbk has signature int& so it means it could modify t. What is not allowed.
Solution:
auto job = [clbk, t]() mutable // keyword 'mutable' added
{
clbk(t); // clbk can change t
};
or make function taking const int& as parameter - then t can be only read.
Demo
I am very new to C++ and I was trying out templating to understand how it works. I have a template function that accepts an argument of type T. The problem that I am facing is that T's type is determined at runtime depending on the value of T and the compiler throws an error because it determines the type without considering the if-else-if-else block.
#include <iostream>
using namespace std;
class MyClass {
public:
void setInt(int x) {}
void setString(string y) {} // copy string object
};
void f1() {
cout << "break" << endl;
}
template<typename T, typename... Args>
void f1(T arg, Args... args) {
string _type(typeid(arg).name());
cout << __PRETTY_FUNCTION__ << endl;
cout << _type << endl;
MyClass c1;
if( _type.compare("i") == 0 ) {
c1.setInt(arg);
} else if ( _type.compare("PKc") == 0 ) {
//c1.setString(arg);
}
f1(args...);
};
int main() {
f1(7, 3.3, "asd", 0xa1);
return 0;
}
The output:
prog.cpp: In instantiation of ‘void f1(T, Args ...) [with T = const char*; Args = {int}]’:
prog.cpp:30:4: recursively required from ‘void f1(T, Args ...) [with T = double; Args = {const char*, int}]’
prog.cpp:30:4: required from ‘void f1(T, Args ...) [with T = int; Args = {double, const char*, int}]’
prog.cpp:35:24: required from here
prog.cpp:25:13: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
c1.setInt(arg);
^~~
prog.cpp:6:19: note: initializing argument 1 of ‘void MyClass::setInt(int)’
void setInt(int x) {}
~~~~^
https://ideone.com/the4AP (The link to online compiler)
One possible approach:
void f1_helper(int arg, MyClass* c) {
c->setInt(arg);
}
void f1_helper(string arg, MyClass* c) {
c->setString(arg);
}
template<typename... Args>
void f1(Args... args) {
MyClass c1;
auto _ = {(f1_helper(args, &c1), 0) ...};
}
Demo
I am following this code snippet which makes it easier to pass a member function to an interface expecting a C-style callback (that is, the interface expects a function pointer to the callback, and a void* pointer to user data which will in turn be passed to the callback). Effectively I want to convert Helper::M to Helper::V below.
I am trying to modify the snippet to automatically deduce the template parameters. Here is my current attempt.
#include <iostream>
template <typename R, typename T, typename... Args>
struct Helper {
using V = R (*)(void*, Args...);
using M = R (T::*)(Args...);
template <M m>
static R Fn(void* data, Args... args) {
return (static_cast<T*>(data)->*m)(std::forward<Args...>(args...));
}
};
template <typename R, typename T, typename... Args>
typename Helper<R, T, Args...>::V Cast(R (T::*m)(Args...)) {
return Helper<R, T, Args...>::template Fn<m>;
}
int CIntf(void* data, int (*f)(void*, int)) { return f(data, 1); }
struct UserData {
int x;
int Add(int y) { return x + y; }
};
int main(int argv, char** argc) {
UserData data = {4};
// Explicit parameters; works.
std::cout << CIntf(&data, Helper<int, UserData, int>::Fn<&UserData::Add>)
<< "\n";
// Deduced parameters; fails.
std::cout << CIntf(&data, Cast(&UserData::Add)) << "\n";
return 0;
}
I tried to compile with gcc -std=c++11 -lstdc++. The explicit parameters method works fine, but the deduced parameters method gives the following error:
tmp.cc: In instantiation of ‘typename Helper<R, T, Args>::V Cast(R (T::*)(Args ...)) [with R = int; T = UserData; Args = {int}; typename Helper<R, T, Args>::V = int (*)(void*, int)]’:
tmp.cc:30:58: required from here
tmp.cc:15:42: error: no matches converting function ‘Fn’ to type ‘using V = int (*)(void*, int) {aka int (*)(void*, int)}’
return Helper<R, T, Args...>::template Fn<m>;
^~~~~
tmp.cc:8:12: note: candidate is: template<int (UserData::* m)(int)> static R Helper<R, T, Args>::Fn(void*, Args ...) [with R (T::* m)(Args ...) = m; R = int; T = UserData; Args = {int}]
static R Fn(void* data, Args... args) {
Note that it correctly deduced the template parameters, but failed to convert Helper<int, UserData, int>::Fn<m> to int (*)(void*, int); why? This same conversion succeeded in the explicit case (unless m is somehow different from &UserData::Add).
Unfortunately you'll have to use a macro for this:
#define makeFunc(method) &Helper<decltype(method)>::Fn<method>
And redefine your helper like this for it to work:
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R(T::*)(Args...)>
The reason why you can't use deduction for this, is that deduction only works on function arguments which are run-time values. And you need to use a method's address as template argument which should be a compile-time value.
So when you do this:
return Helper<R, T, Args...>::template Fn<m>;
you are passing a run-time value m as a template argument which is impossible.
For reference, here is the complete code using the macro. Also note the use of std::forward in the original code was incorrect for multiple arguments (see this answer).
#include <iostream>
#include <utility>
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R (T::*)(Args...)> {
template <R (T::*m)(Args...)>
static R Fn(void* t, Args... args) {
return (static_cast<T*>(t)->*m)(std::forward<Args>(args)...);
}
};
#define VOID_CAST(m) &Helper<decltype(m)>::Fn<m>
struct UserData {
int x;
int Add1(int y) { return x + y; }
int Add2(int y, int z) { return x + y + z; }
};
int Call1(void* data, int (*f)(void*, int)) { return (*f)(data, 1); }
int Call2(void* data, int (*f)(void*, int, int)) { return (*f)(data, 1, 2); }
int main() {
UserData data = {4};
std::cout << Call1(&data, VOID_CAST(&UserData::Add1)) << "\n";
std::cout << Call2(&data, VOID_CAST(&UserData::Add2)) << "\n";
return 0;
}
How can I have this effect without the arbitrary typedefs?
#include <type_traits>
#include <iostream>
typedef int Primary;
typedef float Secondary;
template<Class C, std::enable_if<std::is_same<Class, Primary>::value || std::is_same<Class, Secondary>::value> = 0>
class Entity {
public:
template<std::enable_if<std::is_same<Class, Secondary>::value>::type = 0>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
int main() {
Entity<Secondary> e;
e.onlyLegalForSecondaryEntities();
return 0;
}
Is there a more elegant way to produce this so that Entity can only be instantiated with Primary or Secondary as template arguments?
After fixing the errors in your code:
In C++1z you can easily roll a trait is_any with std::disjunction:
template<typename T, typename... Others>
struct is_any : std::disjunction<std::is_same<T, Others>...>
{
};
In C++11, you can implement disjuncation as
template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional<B1::value != false, B1, disjunction<Bn...>>::type { };
Then define your class template as
template<class C, typename std::enable_if<is_any<C, Primary, Secondary>::value>::type* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
You can take this further and make enable_if_any alias that would resolve to void if possible:
template<typename This, typename... Elems>
using enable_if_is_any = typename std::enable_if<is_any<This, Elems...>::value>::type;
template<class C, enable_if_is_any<C, Primary, Secondary>* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
Using gcc-4.8 with -std=c++11 I want to create a template function with one behaviour for enums and other behaviour for all other types. I try this
#include <type_traits>
#include <iostream>
template<class T, class = typename std::enable_if<std::is_enum<T>::value>::type>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, class = typename std::enable_if<!std::is_enum<T>::value>::type>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}
but compiler returns
1.cpp:11:6: error: redefinition of ‘template<class T, class> void f(T&)’
void f(T& /*t*/) {
^
1.cpp:5:6: error: ‘template<class T, class> void f(T&)’ previously declared here
void f(T& /*t*/)
^
I can comment out first template, it leads to compile error, and it's expectable.
And I also can comment out second template, in this case code code can be compiled.
What do I do wrong?
Because compiler sees them as the same function template, instead, you should do this:
#include <type_traits>
#include <iostream>
template<class T, typename std::enable_if<std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, typename std::enable_if<!std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}