Hi please help me solve a question about template


#1

I just solve using int portion of this question, but int* and others I cant solve.
Please help me, Thanks a lot!!!

test code:

#include <iostream>

template<int i>
void test1(){ std::cout << i << '\n';}

template< char* i>
void test2(){ std::cout << *i << '\n';}
char ci[] = "test";

template<int* i>
void test3(){ std::cout << *i << '\n';}
int i = 100;
int*  ip = &i;

int main() {

    test1<200>(); 
    test2<ci>(); 
   // test3<ip>(); //fail to complie
    return 0;
}

#2

It fails to compile because ip isn’t a compile-time constant.
All template arguments must be compile-time constants.
In this case, to trigger the correct template you can static_cast nullptr,
as I will demonstrate in a moment…


This is either a trick question or the author doesn’t know what static means.
(I’m going to assume it’s a trick question designed to test your knowledge,
or, since I’m the one providing the answer, my knowledge. :P)

There’s no such thing as a pointer to a static member function because a static member function behaves like a normal function,
so to point to a static member function you’d use a normal function pointer.

Similarly a pointer to a static class member would just be a normal pointer.

Pointers to non-static member functions are however separate entities with special syntax, as I will now demonstrate:

#include <iostream>

template<int i>
void test_int()
{
	std::cout << i << '\n';
}

template<int * pointer>
void test_int_pointer()
{
	std::cout << pointer << '\n';
}

class some_class
{
public:
	int member_function()
	{
		return 0;
	}

	static int static_member_function()
	{
		return 0;
	}

	static int static_member_variable;
};

int some_class::static_member_variable = 0;

template<typename Type, int (Type::* member)()>
void test_member_function_pointer()
{
	std::cout << member << '\n';
}

template<int (* function)()>
void test_function_pointer()
{
	std::cout << function << '\n';
}

int main()
{
	test_int<100>();
	test_int_pointer<static_cast<int*>(nullptr)>();
	test_member_function_pointer<some_class, &some_class::member_function>();
	test_function_pointer<some_class::static_member_function>();
	test_int_pointer<&some_class::static_member_variable>();
	std::cin.get();
}

The reason a pointer to a member function is different from a normal function pointer is because of the way the this pointer is implemented.

When you write object.function(), it’s actually implemented like function(object) as if function were defined as void function(Object * this).
C++'s is designed to keep the this parameter implicit, and to make it difficult to provide an invalid object for this.

The kind of function call used on member functions is sometimes called a ‘thiscall’.


#3

Thanks Pharap!!!
You are so great!!
Im outside now, and I will think of and run it tommorrow :sunny:


#4

Hi Pharap:
if non-type parameters must be a complie-time constant,
Why " test_member_function_pointer<some_class, some_class::member_function>();" can be complied;
I think a normal member function needs ‘this’ - a runtime pointer.


#5

Because the address of any function is a compile time constant.
(Similarly the address of any variable with ‘static storage duration’ (chinese version) is a compile time constant.)

A member function is still a compile time constant because the function itself doesn’t change.
The implicit this parameter is given to the function like an argument.

E.g. consider this class:

class A
{
public:
	int value;

	int get_value() const
	{
		return this->value;
	}
};

Assuming A a {};, int b = a.get_value();,
the compiler would translate the code into something like this:

class A
{
public:
	int value;
};

int A_get_value(const A * this)
{
	return this->value;
}

The function itself doesn’t change.
The this pointer given to the function changes.


#6

As you said:
int member_function(){return 0;} has a implicited parameter - ‘this’,
looks like int member_function(some_class* this){return 0;}
and ‘this’ is a runtime object pointer, so if I want to using this normal member function pointer, firstly must create a object.
but as your code
test_member_function_pointer<some_class, &some_class::member_function>();
seem not create a object of some_class ?!
Im full of chao:~

And why the template test_member_function_pointer needs typename Type parameter, template test_function_pointer not need it?


#7

If you want to call the function, yes.
But you don’t have to call a function to know where it is.
Getting the address of a function does not ‘use’ the function.

A strange analogy: I can tell you where the teapot is without pouring tea.

Member function pointers use different syntax because of the special this parameter.
The function needs to know what type the hidden this parameter is.

void (T::*)() specifies that the type of this is T *.
It’s a bit like how void (*)(int) specifies that the first argument is int.


Also, the way non-member function pointers and member function pointers are called is different…

Normal function pointers:

void say_hello()
{
	std::cout << "Hello\n";
}

void say_goodbye()
{
	std::cout << "Goodbye\n";
}

int main()
{	
	void (*function_pointer)() = say_hello;
	
	function_pointer();
	
	function_pointer = say_goodbye;
	
	function_pointer();
	
	std::cin.get();
}

Member function pointers:

class greeter
{
private:
	const char * name;
	
public:
	greeter(const char * name) : name(name) {}

	void say_hello()
	{
		std::cout << "Hello " << this->name << '\n';
	}

	void say_goodbye()
	{
		std::cout << "Goodbye " << this->name << '\n';
	}
};

int main()
{	
	greeter object { "Dave" };
	
	void (greeter::*member_function_pointer)() = &greeter::say_hello;
	
	// object is secretly passed as 'this' to member_function_pointer
	(object.*member_function_pointer)();
	
	member_function_pointer = &greeter::say_goodbye;
	
	(object.*member_function_pointer)();
	
	std::cin.get();
}

The syntax for function pointers is quite confusing. It’s usually easier to use an alias:

using function_pointer = void (*)();
using member_function_pointer = void (greeter::*)();

#8

Thanks I’ve understood a bit of it.

eg and my thinking and question

#include <iostream>

template<int * pointer>
void test_int_pointer()
{
	std::cout << pointer << '\n';
}

class some_class
{
public:
	int member_function()
	{
		return 999;
	}
};

template<typename Type, int (Type::* member)()>
void test_member_function_pointer()
{
        //there is just tell complier the detail type of this pointer(Type:: tells the complier the type of  this), not using it. so it can be compile.
        //there output the pointer's size - 1 storge unit. (not using the member function)
	std::cout << member << '\n'; 
}


int main()
{
	test_member_function_pointer<some_class, &some_class::member_function>();
	test_member_function_pointer<some_class, some_class::member_function>();
        //why add '&' or nothing before 'some_class::member_function' has the same result ???????

	return 0;
}

#9

I’m not sure what you’re referring to with size - 1.

Technically the & is actually necessary for member pointers,
and the compiler should error without it.
In Visual Studio I get:

Error C3867 ‘some_class::member_function’: non-standard syntax; use ‘&’ to create a pointer to member

Some compilers allow the & to be omitted as a non-standard compiler extension.

However, for non-member function pointers, the & is optional…

#include <iostream>

class some_class
{
public:
	int member_function()
	{
		return 999;
	}

	static int static_member_function()
	{
		return 666;
	}
};

template<typename Type, int (Type::* member)()>
void test_member_function_pointer()
{
	std::cout << member << '\n';
}

template<int (* function)()>
void test_function_pointer()
{
	std::cout << function << '\n';
}


int main()
{
	// '&' is required
	test_member_function_pointer<some_class, &some_class::member_function>();

	// This should give an error:
	// test_member_function_pointer<some_class, some_class::member_function>();

	// '&' is optional
	test_function_pointer<some_class::static_member_function>();
	test_function_pointer<&some_class::static_member_function>();

	return 0;
}

#10

std::cout << function << ‘\n’; resulted number 1, so I guessed the ‘function’ be considered 1 size storage unit.
Im not sure😓 haha


#11

Ah, I know what the problem is.
Apparently std::cout can’t print member function pointers.
For some reason member is being implicitly converted to bool.
(If you put std::cout << std::boolalpha; at the start of main, you get true instead of 1.)

StackOverflow confirms this:


#12

thanks Pharap:+1: