C++: request for member is ambiguous - c++

#include <iostream>
using namespace std;
class c1{
public:
void f(){std::cout<<"In f1\n";}
};
class c2{
public:
void f(){std::cout<<"In f2\n";}
};
template <typename T>
class c:public c1, c2 {
T* pT;
public:
T* operator->() { return pT; }
};
int main()
{
c<c1>* cMain;
cMain->f();
return 0;
}
Error:
g++ cache.cc
cache.cc: In function ‘int main()’:
cache.cc:22:8: error: request for member ‘f’ is ambiguous
cMain->f();
^
cache.cc:9:8: note: candidates are: void c2::f()
void f(){std::cout<<"In f2\n";}
^
cache.cc:5:8: note: void c1::f()
void f(){std::cout<<"In f1\n";}
why is it not accessing the function f of c1 as specified in the template? is my template usage wrong? How can i make it point to the required class using template.?

The problem is that you're using pointers. You aren't calling c::operator-> but just dereferencing cMain which is uninitialized and leads to UB. Removing the pointers (for the most part) fixes the problem:
#include <iostream>
class c1{
public:
void f(){std::cout<<"In f1\n";}
};
class c2{
public:
void f(){std::cout<<"In f2\n";}
};
template <typename T>
class c { // No longer derives from c1 and c2
T t; // No longer a pointer
public:
T* operator->() { return &t; }
};
int main()
{
c<c1> cMain; // No longer a pointer
cMain->f(); // Calls c::operator->() now
return 0;
}
see it work
Of course you don't need c to derive from c1 and c2 (or anything) for this to work.

If you don't particularly need the pointer semantics for something else, you can add a function to c that redirects to the proper base class function:
template <typename T>
class c:public c1, c2 {
public:
void f() { T::f(); }
};

Your calling code is equivalent to
c<c1>* cMain;
(*cMain).f(); // equivalent to: c<c1> cMain; cMain.f();
Value *cMain has type of c<c1>. Obviously, now a call to f() is ambiguous, because both c1 and c2 has f() function and c<c1> inherits them both. Templates are not to blame (you can make class c non-template and get the same result).
If you want to call -> operator you should say (*cMain)->f(); but don't forget to initialize the pointer before.

Related

Variadic Template Class not fully expanded

I am trying to create something similar to a type-safe multiqueue. The idea is that when I push an item, it will be added to a queue consisting of objects of the same type.
All the queues will have a common interface(the queue_intf in this case) that will do some processing.
The code I came up is the following:
#include <queue>
#include <vector>
#include <memory>
#include <stdlib.h>
class queue_intf {
public:
virtual void process(void) = 0;
};
template <typename T>
class queue : public queue_intf {
public:
std::queue<T> q_;
static const char* name()
{
return typeid(T).name();
}
virtual void process(void) override {
printf("process: %s\n", this->name());
}
void push(T &a) {
printf("push: %s\n", this->name());
}
};
template <typename...>
class queues {
public:
std::vector<queue_intf *> qs_;
void process(void) {
for (auto q: this->qs_) {
q->process();
}
}
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
queue<T> q_;
queues() {
this->qs_.push_back(&this->q_);
}
void push(T &v) {
q_.push(v);
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.process();
qs.push(bi);
}
However when I compile it I get the following error:
main.cc: In function ‘int main(int, char**)’:
main.cc:65:15: error: no matching function for call to ‘queues<a, b>::push(b&)’
qs.push(bi);
^
main.cc:45:10: note: candidate: void queues<T, Ts ...>::push(T&) [with T = a; Ts = {b}]
void push(T &v) {
^~~~
main.cc:45:10: note: no known conversion for argument 1 from ‘b’ to ‘a&’
I was expecting the queue class to have void push(b &v) method, but it seems it doesn't.
Any idea why?
Edit:
Here is a smaller example (no std:vector or anything):
template <typename...>
class queues {
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
void push(T &v) {
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.push(bi);
}
To explain this, let's use a simplified example, you have just two classes:
class base {
public:
void method(int *);
};
class derived : public base {
public:
void method(char *);
};
And you attempt to call it:
derived d;
int i;
d.method(&i);
This fails. Name lookup starts at the given derived class, and finds a matching class method with the specified name: method. However its parameter doesn't match, so this is ill-formed. If the derived class had both methods, overload resolution will pick the matching one. But once a class method with the specified name is found any parent class will not be searched for any other methods with the same name. Only if the derived class does not have any methods with the specified name does name lookup continue with the parent class(es).
Putting
using base::method;
In the derived class imports base's method into the derived's namespace, at which point everything works just like with regular overload resolution.
This is why you need a using declaration with your recursive templates. The second derived template imports it from the first one, the next one imports everything from the second derived template, and so on.

How to get a C++ template class to invoke another class' methods?

I have class A that needs to invoke the member functions of template class B. Searching around I found this sample code on this site:
#include <iostream>
template<typename T, typename FType>
void bar(T& d, FType f) {
(d.*f)(); // call member function
}
struct foible
{
void say()
{
std::cout << "foible::say" << std::endl;
}
};
int main(void)
{
foible f;
bar(f, &foible::say); // types will be deduced automagically...
}
That came from this answer:
C++ method name as template parameter
But it doesn't do 100% of what I need. How would the above code be re-written so that:
method bar is a public member of a class and not a stand-alone
function
arguments d and f which are getting passed to method bar are
public members of the same class to which bar is a member,
allowing bar to be of type void (void)
object type foible is a class and not a structure (optional)?
[EDIT 1] My own attempt at the rewrite which meets points 1) and 2) is the following, which is wrong:
#include <iostream>
template<class T, void (T::*FType)()>
class foo {
public:
T obj;
FType f;
void bar(void) {
(obj.*f)(); // call member function
} // end bar
}; // end class foo
struct foible
{
void say()
{
std::cout << "foible::say" << std::endl;
}
};
int main(void)
{
foible f;
foo<foible, void (foible::*)()> c;
c.T = f;
c.Ftype = &foible::say;
c.bar(); // types will be deduced automagically...
}
My goal is to have an object of class type 'A' invoke the methods of object of class type 'B', so that when these methods execute the object of type 'B' can use its 'this' pointer to reference its local data. I want to use function pointers inside class type 'A' so that these only need to be specified once, and I don't want one class to have to be derived from another.
You are making that too complicated. Forget about pointers to methods. Currently there is no reason to use them.
Just do something like this:
template<typename F>
void bar(F f) {
doSomething();
f(someArg);
doSomething();
}
int main(void)
{
foible f;
bar([&f](auto x) { f.someMagicMethod(x); } );
return 0;
}
Note this approach is more flexible and readable than playing around with method pointers.
A step by step solution:
all examples uses the following class and foo function
#include <iostream>
class A
{
public:
void foo(){std::cout<<"foo"<<std::endl;}
};
this sample works without template: just calling the calling A::foo with pointer to A and pointer to A::foo:
class B
{
public:
A *a;
void (A::*p)();
void bar()
{
(a->*p)(); //call A::foo
}
};
int main(void)
{
A a;
B b;
b.a = &a;
b.p = &A::foo;
b.bar();
return 0;
}
The following sample added template class T, the pointer to foo method derived from T.
template <class T>
class C
{
public:
T* t;
void (T::*p)();
C(T &o) : t(&o){}
void bar()
{
(t->*p)();
}
};
int main(void)
{
A a;
C<A> c(a);
c.p = &A::foo;
c.bar();
return 0;
}
in the following, the method pointer was templated too, but I don't see how can it be used since you should know how many argument to give it, but anyway:
template <class T, typename F>
class D
{
public:
T* t;
F p;
D(T &o, F pf) : t(&o),p(pf){}
void bar()
{
(t->*p)();
}
};
int main(void)
{
A a;
D<A, void (A::*)()> d(a, &A::foo);
d.bar();
return 0;
}

Refer to function of different base class according to template parameter

#include <iostream>
using namespace std;
class c1 {
public:
void f1() { std::cout << "In f1\n"; }
};
class c2 {
public:
void f2() { std::cout << "In f2\n"; }
};
template<typename T>
class C: public c1, c2 {
public:
void f() {
};
};
int main() {
C<c2> c;
c.f();
return 0;
}
is there any way based on T the function f in C can be mapped to function f1 in c1 and f2 in c2? I am not clear how function f that can be used as a wrapper around f1 and f2 when i am pointing to a specific class using T
Note: I cannot modify class c1 and c2. its out of my scope.
You can use constexpr if from C++17. e.g.
void f() {
if constexpr (std::is_same_v<T, c1>)
f1();
else
f2();
}
LIVE
Note that constexpr if is evaluated at compile-time, as #skypjack commented, for this case, it's pretty fine to be evaluated at run-time too. So the following code works fine too:
void f() {
if (std::is_same_v<T, c1>)
f1();
else
f2();
}
Yes, you can write a template like that. In several ways in fact. If you want to keep C as it is, then it's a simple matter of adding a type trait which contains a pointer to a member function
Live Example
template<typename> struct which_member;
template<> struct which_member<c1> {
static constexpr void (c1::* func)() = &c1::f1;
};
template<> struct which_member<c2> {
static constexpr void (c2::* func)() = &c2::f2;
};
void f() {
(static_cast<T*>(this)->*which_member<T>::func)();
}
The key is here (static_cast<T*>(this)->*which_member<T>::func)();. We cast this to the appropriate type pointer, then use the trait to retrieve the pointer to a member. And finally we use the access operator ->* on these two operands to obtain a callable expression. Which we then call (the outer ()).
The cast isn't strictly needed, but I think the error message is more descriptive of the problem if you pass something that isn't a base class of C as the type parameter.
You can specialise template:
#include <iostream>
using namespace std;
class c1{
public:
void f1(){std::cout<<"In f1\n";}
};
class c2{
public:
void f2(){std::cout<<"In f2\n";}
};
template <typename T>
class C:public c1, c2 {
public:
void f()
{
};
};
template<> class C<c1>:public c1, c2 {
public:
void f()
{
c1::f1();
};
};
template<> class C<c2>:public c1, c2 {
public:
void f()
{
c2::f2();
};
};
int main()
{
C<c2> c;
c.f();
return 0;
}

How to use template parameter to choose method call?

I have a method which is templated and I want it to call a different method depending on the template. The reason I have this is so that the caller does not need to create an Object of type B just to get the correct implementation called, instead they should be able to just choose by the implementation by templating.
The problem is i'm receiving reference type to a const as the template T and I do not know how to use this to choose the correct overloaded method. Ideally this would also work if T were not a reference type. Any idea?
Note: I can't use template specialization because I need the impl virtual.
#include <iostream>
using namespace std;
class A {};
class B {};
class C {
public:
template <typename T>
void f() {
// T = const B&
impl(T()); // error: value-initialization of reference type ‘const B&’
}
protected:
virtual void impl(const A& a) {
cout << "A";
}
virtual void impl(const B& b) {
cout << "B";
}
};
int main() {
C c;
const B &b2 = B();
c.f<decltype(b2)>(); // T = const B&
return 0;
}
If you want to get a type [more] likely to be constructible, you should remove any references, e.g., using std::remove_reference<T> and all qualifiers, e.g., using std::remove_cv<T>. ... or, just std::decay<T> the type which also transforms arrays into pointers:
template <typename T>
void f() {
impl(typename std::decay<T>::type());
}
You can still use template specialization through a helper private template dispatcher to call correct version of impl - which can still be virtual. Like this, compiles and runs
#include <iostream>
using namespace std;
class A {};
class B {};
class C {
public:
template <typename T>
void f() {
// T = const B&
impl_dispatch<T>(); // error: value-initialization of reference type ‘const B&’
}
protected:
virtual void impl(const A& a) {
cout << "A";
}
virtual void impl(const B& b) {
cout << "B";
}
private:
template <typename T>
void impl_dispatch();
};
template <> void C::impl_dispatch<A const &>()
{
impl(B());
}
template <> void C::impl_dispatch<B const &>()
{
impl(A());
}
int main() {
C c;
const B &b2 = B();
c.f<decltype(b2)>(); // T = const B&
return 0;
}

c++ template and inheritance with method pointer

if I declare:
class Avoidance : public Schema<std_msgs::String,prog1::Command>{
and I try to
void*(Schema<std_msgs::String,prog1::Command>::*pt)();
pt=&Avoidance::frontBusy;
compiler report me
error: cannot convert ‘void* (Avoidance::*)()’
to
‘void* (Schema<std_msgs::String_<std::allocator<void> >, prog1::Command_<std::allocator<void> > >::*)()’ in assignment
why? Avoidance inherits from
Schema<std_msgs::String,prog1::Command>
then Avoidance IS Schema<.....>
That's not how member function pointers work. If frontBusy is a base function, you need to type the pointer appropriately. The dispatch will still work as expected, though!
Here's a basic example:
struct A { virtual void f() = 0; };
struct B : A { virtual void f() { } };
void dispatch(void (A::*pf)(), A & a)
{ // ^^^^^
(a.*pf)();
}
int main()
{
B x;
dispatch(&A::f, x); // calls x.B::f()
} // ^^^^^
So, in your case you want:
void (Schema<std_msgs::String,prog1::Command>::*p)()
= &Schema<std_msgs::String,prog1::Command>::frontBusy;
Getting rid of templates to simplify, suppose you have
class B {
public:
void f();
};
class D : public B {
public:
void g();
};
It might seem a little backwards at first, but you can cast void (B::*)() to void (D::*)(), but you cannot cast void (D::*)() to void (B::*)(). This makes sense when you think about how they would later be used.
void test() {
void (D::*p)() = &B::f; // OK!
void (B::*q)() = &D::g; // ERROR!
B b;
D d;
(d.*p)(); // Calls B::f on `d`. Okay, `B::f` is an inherited member.
(b.*q)(); // Calls D::g on `b`?? But that's not a member of `b` at all!
}