Lets say I have the follow code
template<class MemberFunc>
class Foo {
MyClass object_;
void call() {
auto ptr = MemberFunc{};
(object_.*ptr)();
}
};
int main() {
Foo<decltype(&MyClass::doThings)> foo;
foo.call();
}
This code does crash for me because ptr is 0. Why does the member function constructor returns 0?
My workaround is the following but it involves code duplication. Is there no other way to just construct/instantiate the member function from the type? C++14 welcome.
template<class MemberFunc, MemberFunc f>
class Foo {
MyClass object_;
void call() {
(object_.*f)();
}
};
int main() {
Foo<decltype(&MyClass::doThings), &MyClass::doThings> foo;
foo.call();
}
Why does the member function constructor returns 0?
Because it's a pointer (-to-member-function), and all scalar types value-initialize to 0 (C++14 [dcl.init]/8.4).
Is there no other way to just construct/instantiate the member function from the type?
You can have multiple member functions with the same signature; how would it know which member function you want to refer to?
The code you have is fine for C++14. In C++17, it can be shortened to the following:
template<auto f>
class Foo {
MyClass object_;
void call() {
(object_.*f)();
}
};
int main() {
Foo<&MyClass::doThings> foo;
foo.call();
}
You can't instantiate a member function from its type. For example, consider the following class:
struct foo
{
void bar(int){}
void baz(int){}
};
Suppose you have a type void (foo::*)(int). Which function would you like to get from it?
As of C++1z, you'll be able to use auto to deduce non-type, non-template template parameters:
template<auto f>
class Foo {
MyClass object_;
void call() {
(object_.*f)();
}
};
int main() {
Foo<&MyClass::doThings> foo;
foo.call();
}
demo
The only workaround for C++11/14 I can think of is using a macro:
#define type_value_pair(x) decltype(x), x
template<class MemberFunc, MemberFunc f>
class Foo {
MyClass object_;
void call() {
(object_.*f)();
}
};
int main() {
Foo<type_value_pair(&MyClass::doThings)> foo;
foo.call();
}
demo
But I'd advise against using this, for readability reasons.
decltype returns the type of its argument. Using the following example:
class MyClass {
public:
int foo();
int bar();
};
Both
decltype(&MyClass::foo);
and
decltype(&MyClass::bar);
is the same type: int (MyClass::*)().
When you default-initialize this type, the default initialization results in a nullptr. Hence the crash.
When you value-initialize a pointer to member-function, you will receive a nullptr, of course.
By the way, you can have multiple member function with the same type as you can see in Sam's answer.
I don't know why do you need to put the MemberFunc to template parameter list of Foo.
However, if you want to make a function to call it, this maybe a better approach in C++ before C++1z (use auto in C++1z is better match for this question):
class Foo {
MyClass object_;
public:
template<class MemberFunc>
void call(MemberFunc ptr) {
(object_.*ptr)();
}
};
int main() {
Foo foo;
foo.call(&MyClass::doThings);
}
Related
I'm having a hard time understanding why the following snippet compiles. I have a template class ptr_to_member<T> which stores a pointer to a member function of T. I am then creating a new class my_class which has a ptr_to_member<my_class>. I expected the latter to cause a compilation error given that my_class is still being defined. Any help is appreciated. Thank you.
#include <iostream>
// this class simply stores a pointer to a member function of T
template <class T>
struct ptr_to_member {
using ptr_type = void (T::*)();
ptr_type m_ptr;
ptr_to_member()
: m_ptr(&T::f){}
auto get_ptr() const {
return m_ptr;
}
};
// my_class has a ptr_to_member<my_class>
class my_class {
ptr_to_member<my_class> m_ptr_to_member; // why does this compile?
public:
void g() {
auto ptr = m_ptr_to_member.get_ptr();
(this->*ptr)();
}
void f() {
std::cout << "f" << std::endl;
}
};
int main() {
my_class x;
x.g();
}
While defining a class, that class can be used as if it were forward declared. Since ptr_to_member only uses pointers to T until you instantiate one of it's methods, it doesn't complain. Try adding a member T rather than a pointer and you will see that it fails. The intuitive way of seeing it is that you don't need to know what T is to use a pointer of T, only that T is a type. Pointers behave the same way regardless of what they point to until you dereference them.
template <class T>
struct ptr_to_member {
using ptr_type = void (T::*)();
ptr_type m_ptr;
ptr_to_member()
: m_ptr(&T::f) {}
auto get_ptr() const {
return m_ptr;
}
T member; // Add this and see that it fails to compile
};
See this answer for more information on when you can and can't use forward declared types.
I found detect class 's member use global template function is not work:
void printinfo(std::true_type)
{
cout<<"true_type!";
}
void printinfo(std::false_type)
{
cout<<"false_type!";
}
class TestAA
{
public:
void foo();
};
class TestBB;
template<typename T,typename =decltype(&T::foo)>
std::true_type havefoo(T*){return{};}
std::false_type havefoo(...){return{};}
int main()
{
printinfo(havefoo((TestAA*)nullptr));
printinfo(havefoo((TestBB*)nullptr));
}
class TestBB
{
public:
void foo();
};
it fail detect TestBB's foo,is normal? or a complier bug? gcc 4.8.1
The compiler hasn't seen the definition of TestBB at the point of the call to printinfo, only the forward declaration. At that point it doesn't know about any members of TestBB.
From a blog post Access to private members: Safer nastiness by Johannes Schaub - litb:
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
// use
struct A {
A(int a):a(a) { }
private:
int a;
};
// tag used to access A::a
struct A_f {
typedef int A::*type;
friend type get(A_f);
};
template struct Rob<A_f, &A::a>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
how get function can be call from a object since its not defined inside class A ?
EDIT:
I don't understand why get must have Tag as parameter instead of a.*get<A_f>()
=> ok it's due to ADL mechanism
You are not calling get from a! Actually what get return is a class pointer to a member inside A and type of it is int A::* so you need an instance of A to access that value.
For example let me play a little with your code:
struct A {
A(int a):a(a) { }
int b;
private:
int a;
};
void test() {
auto p = &A::b;
std::cout << a.*p << std::endl;
}
Did I call p from inside a? a does not have p, this is exactly what happened in your code, get function return &A::a and you use a to read its value! that's all, nothing is wrong and I think it will be compiled in all compilers.
One other question here is: Why C++ allow declaring template using private member of A. C++ standard say:
14.7.2p8 The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template
arguments and names used in the function declarator (including
parameter types, return types and exception specifications) may be
private types or objects which would normally not be accessible and
the template may be a member template or member function which would
not normally be accessible.]
But if you try to instantiate or even typedef specified template then you get an error.
Let's modify your example slightly:
struct A {
private:
int a;
friend void f();
};
// Explicit instantiation - OK, no access checks
template struct Rob<A_f, &A::a>;
// Try to use the type in some way - get an error.
struct Rob<A_f, &A::a> r; // error
typedef struct Rob<A_f, &A::a> R; // error
void g(struct Rob<A_f, &A::a>); // error
// However, it's Ok inside a friend function.
void f() {
Rob<A_f, &A::a> r; // OK
typedef Rob<A_f, &A::a> R; // OK
}
It's legal because friend functions are always in the global scope, even if you implement them inside a class. In other words, this:
class A
{
friend void go() {}
};
is just a shortcut for:
class A
{
friend void go();
};
void go() {}
This is a known compiler bug in gcc and was fixed in a later release.
See-:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437
I was trying to integrate the boost::share_ptr into a pair of templated classes that were originally derived from a boost::asio example I found. When I define a type within one class which is a shared::ptr of that class. I can't seem to reference the type in another templated class. If I remove templates from the code, it all compiles.
This won't compile:
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace std;
template <typename TSomething1>
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT<TSomething1> >
{
public:
typedef boost::shared_ptr<SomeTemplateT<TSomething1> > Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT<TSomething1>());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
template <typename TSomething>
class OtherTemplateT
{
public:
OtherTemplateT()
{
// COMPILATION ERROR HERE
SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
}
private:
};
The code above yields the following compilation error:
src\Templates\main.cpp: In constructor 'OtherTemplateT<TSomething>::OtherTemplateT()':
src\comps\oamp\src\Templates\main.cpp:30: error: expected ';' before 'someTemplate'
Taking virtually the same code without templates compiles without difficulty:
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT>
{
public:
typedef boost::shared_ptr<SomeTemplateT> Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
class OtherTemplateT
{
public:
OtherTemplateT()
{
SomeTemplateT::Ptr someTemplate = SomeTemplateT::Create();
}
private:
};
Platform information:
I'm using gcc4.4.0 from MinGW on windows XP (Code:Blocks IDE).
Am I doing something wrong?
EDIT:
I forgot to mention that if I replace the use of the Ptr typedef with the full declaration of the shared ptr:
boost::shared_ptr
Everything compiles fine.
Also, I can use the type in code outside the of the template.
SomeTemplateT<TSomething>::Ptr is a dependent name; that is, its definition depends on the template parameter. The compiler can't assume that it's a type name unless you say so:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
^^^^^^^^
You need to use typename:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
This is required to make parsing possible without semantic analysis. Whether SomeTemplateT<TSomething>::Ptr is a type or a member is not known until SomeTemplateT<TSomething> has been compiled.
A example taken from the C++11 Standard (n3290) that demonstrate why the keyword typename (in this context) is useful.
( 14.6 Name resolution [temp.res] )
struct A
{
struct X { };
int X;
};
struct B
{
struct X { };
};
template<class T> void f(T t)
{
typename T::X x;
}
void foo()
{
A a;
B b;
f(b); // OK: T::X refers to B::X
f(a); // error: T::X refers to the data member A::X not the struct A::X
}
What's wrong with this code? gcc 4.6.1 is complaining "‘foo’ was not declared in this scope" in baz(). If I transform the code so that one of the templates is just a regular class, the problem goes away.
struct Foo {
char foo;
};
template<int N>
struct Bar : public Foo
{
Bar() { foo; }
};
template<int N>
struct Baz : public Bar<N>
{
void baz() { foo; }
};
int main() {
Baz<10> f;
return 0;
}
What is wrong, according to the specifications, I don't know, but you may make your code to compile by using:
void baz() { Bar<N>::foo; }
foo is a dependent name; that is, it depends on the template parameter, so until the template is instantiated the compiler doesn't know what it is. You have to make it clear that it is a class member, either Bar<N>::foo or this->foo.
(You probably also want to do something with it; simply using it as the ignored value of an expression doesn't do anything at all).