C++ Template with class method as parameter - c++

Is it possible to pass a class method as a parameter to a template? For example:
template<typename T, void(T::*TFun)()>
void call(T* x) {
x->TFun();
}
struct Y {
void foo();
};
int main() {
Y y;
call<Y,&Y::foo>(&y); // should be the equivalent of y.foo()
}
If I try compiling the above, I get:
main.cpp: In instantiation of void call(T*) [with T = Y; void (T::* TFun)() = &Y::foo]:
main.cpp:12:23: required from here
main.cpp:4:5: error: struct Y has no member named TFun
x->TFun();
^
Is this possible and if so, what's the syntax?

That's not how you refer to a pointer-to-member. You need to dereference it first:
(x->*TFun)();
I used parenthesis do deal with operator precedence issues. TFun will be dereferenced before it is called.

Related

Cannot get pointer to member funtion out of decltype

I've observed I cannot do &decltype(c)::f to get a pointer to member function f using a class instance c, but I can do &C::f to get that pointer to member function, using the class type C, which I believe is the same as decltype(c).
See this minimal example:
struct C{
int f()
{
return 5;
}
} c;
template<typename T, T t, typename S, S*s> void callCf()
{
(s->*t)();
}
int main()
{
callCf<decltype(&decltype(c)::f),&decltype(c)::f,C,&c>();
}
Compiling this gives:
In function 'int main()':
15:3: error: parse error in template argument list
15:58: error: no matching function for call to 'callCf()'
15:58: note: candidate is:
8:49: note: template<class T, T t, class S, S* s> void callCf()
8:49: note: template argument deduction/substitution failed:
15:58: error: template argument 2 is invalid
Using the following works as expected:
int main()
{
callCf<decltype(&decltype(c)::f),&C::f,C,&c>();
}
Even this works:
int main()
{
using tC = decltype(c);
callCf<decltype(&decltype(c)::f),&tC::f,C,&c>();
}
This also works:
template<typename T>
struct forward_type{
typedef T type;
};
int main()
{
callCf<decltype(&decltype(c)::f),&forward_type<decltype(c)>::type::f,C,&c>();
}
My question is: why is it not possible to use decltype to obtain a pointer to member function like this &decltype(c)::f?
Edit: #Paul Sanders has shown in the comments that the minimal example works in c++17. I'm still using c++14. Does c++17 include some changes to the language that allow my minimal example to compile?
c is an lvalue, so decltype(c) will not return the C type itself, which is why decltype(c)::f does not work. decltype(c) will actually return a C& reference type instead:
If the argument is any other expression of type T, and
...
b) if the value category of expression is lvalue, then decltype yields T&;
...
You can use std::remove_reference/_t to get the C type from C&, eg:
int main()
{
callCf<
decltype(&std::remove_reference_t<decltype(c)>::f),
&std::remove_reference_t<decltype(c)>::f,
std::remove_reference_t<decltype(c)>,
&c
>();
}
Live Demo
Which can then be simplified with a using statement:
int main()
{
using tC = std::remove_reference_t<decltype(c)>;
callCf<decltype(&tC::f), &tC::f, tC, &c>();
}
Live Demo

Is there an analogue of keyword "where" from C# in C++?

I need to create a template class in C++. I need to make sure that the type for the template parameter will be a class with 1 int field and 1 string field (there can be more fields, but these are mandatory).
For example, in C# I could define an interface with methods or properties, like this:
interface MyInterface {
int GetSomeInteger();
string GetSomeString();
}
and then I could use it in my template class:
class MyClass<T> where T: MyInterface {}
Is there any way to do something like this in C++?
C++20 offers you the closest solution to C#:
#include <concepts>
template <class T>
concept MyInterface = requires(T x)
{
{ x.GetSomeInteger() } -> std::same_as<int>;
};
And then:
template <MyInterface T>
struct MyClass
{
// ...
};
The most common way of doing this in current versions of C++ is a technique known as "duck-typing".
It simply involves just using T as if it implements the interface and let the compiler fail if you use the class with an incompatible type.
template<typename T>
class MyClass<T> {
int foo() {
T val;
return val.GetSomeInteger();
}
};
class Valid {
public:
int GetSomeInteger() {return 0;}
};
class Invalid {
};
int main() {
// works fine
MyClass<Valid> a;
a.foo();
// fails to compile
MyClass<Invalid> b;
b.foo();
}
Mind you, there ARE ways of enforcing this a bit more formally, but the amount of code involved is often not worth the benefit.
C++20 has concepts. Some compilers already support them. For example the following with gcc (trunk) -std=c++2a -fconcepts:
#include <string>
#include <iostream>
#include <concepts>
template<typename T>
concept HasGetIntAndString = requires(T& a) {
{ a.GetSomeInteger() } -> std::same_as<int>;
{ a.GetSomeString() } -> std::same_as<std::string>;
};
template <HasGetIntAndString T>
void bar(const T& t){
std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}
struct foo {
int GetSomeInteger() const { return 42; }
std::string GetSomeString() const { return "some"; }
};
struct foo_not {
std::string GetSomeInteger() { return "some"; }
int GetSomeString() { return 42; }
};
int main(){
bar( foo{});
bar( foo_not{});
}
results in:
<source>: In function 'int main()':
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints
28 | bar( foo_not{});
| ^
<source>:12:6: note: declared here
12 | void bar(const T& t){
| ^~~
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':
<source>:28:19: required from here
<source>:6:9: required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]
<source>:6:30: in requirements with 'T& a' [with T = foo_not]
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement
7 | { a.GetSomeInteger() } -> std::same_as<int>;
| ~~~~~~~~~~~~~~~~^~
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement
8 | { a.GetSomeString() } -> std::same_as<std::string>;
| ~~~~~~~~~~~~~~~^~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
Live Demo
Before C++20 you can use SFINAE. However, often it is simpler and more appropriate to not restrict the tempalte parameter more than necessary. If the template does call T::GetSomeInteger() but the type T has no such method, the template will already fail to compile without taking any further measures. SFINAE is mainly to provide nicer error messages.

How to call function template from another function defined earlier?

I have two function templates A and B. I define A and then B in the same file. Now I would like to call B in A. How can I realize this? Normal function prototype doesn't work in this case. (Please assume you cannot change the order of A and B or split files.)
#include <iostream>
template <class Type>
Type A(Type x) {
return 2 * B(x);
}
template <class Type>
Type B(Type x) {
return 3 * x;
}
int main() {
int x = 3;
std::cout << A(x) << "\n"; //=> ERROR
}
ERROR from g++:
test.cpp: In instantiation of ‘Type A(Type) [with Type = int]’:
test.cpp:40:21: required from here
test.cpp:29:17: error: ‘B’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
return 2 * B(x);
~^~~
test.cpp:33:6: note: ‘template<class Type> Type B(Type)’ declared here, later in the translation unit
Type B(Type x) {
^
If by prototype you mean declaration, it certainly does work in this case!
You can declare a function template just fine:
#include <iostream>
// Non-defining declaration B
template <class Type>
Type B(Type x);
// Defining declaration A
template <class Type>
Type A(Type x) {
return 2 * B(x);
}
// Defining declaration B
template <class Type>
Type B(Type x) {
return 3 * x;
}
int main() {
int x = 3;
std::cout << A(x) << "\n"; //=> NO ERROR
}
(live demo)

wrong-looking compile error invoking template member function of template class

I have a templated C++ class with a further template on one of its member functions.
I am calling it in two places in my code, one of them works, the other generates a very confusing error which boils down to the sample code below:
#include <memory>
template <unsigned char N>
struct Foo
{
template <typename OtherFoo, unsigned X>
void do_work (
const OtherFoo * __restrict,
float,
Foo * __restrict
)
const
{
}
};
struct Bar
{
std :: unique_ptr <Foo<0>> foo_0;
std :: unique_ptr <Foo<1>> foo_1;
std :: unique_ptr <Foo<2>> foo_2;
void run (float);
template <typename FOO>
void run (std :: unique_ptr <FOO> & foo, float x)
{
FOO out;
foo -> template do_work <123> (foo_2.get(), x, &out);
}
};
void Bar :: run (float x)
{
if (foo_0)
run (foo_0, x);
else
run (foo_1, x);
}
int main ()
{
Bar bar;
bar .run (1.23);
}
The error message is quite straightforward, but apparently wrong.
temp.cpp: In member function ‘void Bar::run(std::unique_ptr<FOO>&, float) [with FOO = Foo<0u>]’:
temp.cpp:61:16: instantiated from here
temp.cpp:54:3: error: no matching function for call to ‘Foo<0u>::do_work(Foo<2u>*, float&, Foo<0u>*)’
temp.cpp: In member function ‘void Bar::run(std::unique_ptr<FOO>&, float) [with FOO = Foo<1u>]’:
temp.cpp:63:16: instantiated from here
temp.cpp:54:3: error: no matching function for call to ‘Foo<1u>::do_work(Foo<2u>*, float&, Foo<1u>*)’
Let's see, no matching function for call to Foo<1u>::do_work(Foo<2u>*, float&, Foo<1u>*) ...? No, that to me looks EXACTLY like a valid instantiation of Foo::do_work.
Is the compiler wrong? (gcc 4.5.1 on ubuntu 12.04) What's especially weird is that this code does compile in what appears to be an equivalent invocation elsewhere in the code (the full thing has rather too many dependencies to be meaningfully reproduced here).
You should change the order of your template parameters for the do_work<>() function template, or your instantiation will indeed be incorrect:
// template<typename OtherFoo, unsigned X> // This order is not appropriate.
// Let template parameters that
// cannot be deduced come first...
template<unsigned X, typename OtherFoo>
// ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
// THIS FIRST THEN THIS
void do_work(const OtherFoo* __restrict, float, Foo* __restrict) const
{
}
That is because in the following function call you are providing an explicit argument for the first template parameter:
foo->template do_work<123>(foo_2.get(), x, &out);

How to pass inner typedef which is "int X::*" as template function parameter?

In this code, I want to pass the address of x.y as the template parameter typename Name::Type leValue.
#include <iostream>
using std::cout;
using std::endl;
struct X {
X() : y(123) {}
const int y;
};
template<typename Name, typename Name::Type leValue>
void print() { cout << *leValue << endl; }
struct Foo {
typedef int X::* Type;
};
int main() {
X x;
print<Foo, &x.y>(); // What is the right syntax here?
}
However, with gcc 4.7.2, I get the following errors:
source.cpp: In function 'int main()':
source.cpp:22:5: error: parse error in template argument list
source.cpp:22:22: error: no matching function for call to 'print()'
source.cpp:22:22: note: candidate is:
source.cpp:11:6: note: template void print()
source.cpp:11:6: note: template argument deduction/substitution failed:
source.cpp:22:22: error: template argument 2 is invalid
If I instead change the typedef to typedef int Type;, and the print call to print<Foo, 3>();, then it works. I tried several things by looking at the error messages, but could not get the syntax right. I have also searched here, and found some useful posts dealing with template classes, but none dealing with template functions. I tried using those answers but it did not help.
Could you please help me with this syntax, or explain to me what I should try doing next to fix this?
Is this close to what you're looking for?
#include <iostream>
using std::cout;
using std::endl;
struct X {
X() : y(123) {}
const int y;
};
template<typename Name, typename Type, Type Name::*Member>
void print(Type& obj) { cout << obj.*Member << endl; }
int main() {
X x;
print<X, const int, &X::y>(x);
}
The address of x.y is unknown in compile time. You can take a pointer to the member y as template argument, however, you have to pass the address of the object instance in run-time.
It works when you change it to an int because your allowed to pass const ints as template parameters. Templates will not let you pass values as arguments because they need to be resolved at compile time.