How to take address of templated member function instance in C++? - c++

I was trying to take address of templated member function instance. For some reason, it is not working. Here is minimal reproducible example:
class X {
public:
template<bool B>
inline void f() {}
const decltype(f<true>)* truef = f<true>;
const decltype(f<false>)* falsef = f<false>;
};
The above code is giving the following error:
<source>:7:27: error: 'decltype' cannot resolve address of overloaded function
7 | const decltype(f<true>)* truef = f<true>;
| ^
<source>:8:28: error: 'decltype' cannot resolve address of overloaded function
8 | const decltype(f<false>)* falsef = f<false>;
| ^
<source>:7:38: error: cannot resolve overloaded function 'f' based on conversion to type 'const int*'
7 | const decltype(f<true>)* truef = f<true>;
| ^~~~~~~
<source>:8:40: error: cannot resolve overloaded function 'f' based on conversion to type 'const int*'
8 | const decltype(f<false>)* falsef = f<false>;
But the same code works if f wasn't a member function:
template<bool B>
inline void f() {}
constexpr decltype(f<true>)* truef = f<true>;
constexpr decltype(f<false>)* falsef = f<false>;
So, how to take address of templated member function instance in C++?

The correct syntax to get a pointer to a member function would be: &classname::membername so that the program should be modified to as shown below.
Basically, pointer to member functions distinct from ordinary pointers(like pointer to normal functions).
class X {
public:
template<bool B>
inline void f() {}
//-----------------vvvv------------------------------->changed syntax here
const decltype(&X::f<true>) truef = &X::f<true>;
const decltype(&X::f<false>) falsef = &X::f<false>;
};
Demo

Related

Type deduction resullts in ambiguous call of overloaded function

While mixing type deduction with overloading, I stumbled upon a behavior of type deduction for lambda functions that I find difficult to understand.
When compiling this program:
#include <functional>
#include <cstdlib>
int case_0(int const& x) {
return 2*x;
}
int case_1(int& x) {
x += 2;
return 2*x;
}
class Test {
public:
Test(int const n) : n(n) {}
int apply_and_increment(std::function<int(int const&)> f) {
n++;
return f(n);
}
int apply_and_increment(std::function<int(int&)> f) {
return f(n);
}
private:
int n;
};
int main() {
Test t(1);
auto f = [](int const& x) -> int {
return 3*x;
};
t.apply_and_increment(case_0); // Fails compilation
t.apply_and_increment(std::function<int(int const&)>(case_0)); // Succeeds
t.apply_and_increment(case_1); // Succeeds
t.apply_and_increment(f); // Fails compilation
return EXIT_SUCCESS;
}
The output of the compilation is:
$ g++ -std=c++20 different_coonstness.cpp -o test
different_coonstness.cpp: In function ‘int main()’:
different_coonstness.cpp:34:30: error: call of overloaded ‘apply_and_increment(int (&)(const int&))’ is ambiguous
34 | t.apply_and_increment(case_0);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:37:25: error: call of overloaded ‘apply_and_increment(main()::<lambda(const int&)>&)’ is ambiguous
37 | t.apply_and_increment(f);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
As far as I understand:
case_0 is ambiguous because there are 2 valid type conversions, std::function<int(const int&)> and std::function<int(int&)>, and both overloaded functions apply_and_increment() can be applied. This is why the explicit type conversion std::function<int(int const&)>(case_0) is required.
in case_1, the only valid conversion is std::function<int(int&)>, so there is no ambiguity.
I am not very familiar with type deduction and lambdas, so I am a bit surprised that t.apply_and_increment(f) fails to compile. I would expect that the type of the function would be deduced by the type signature, [](int const& x) -> int, in the lambda function.
Why is not f of type std::function<int(int const&)>?
Your understanding of overload resolution for case_0 and case_1 is correct:
A reference-to-non-const can be assigned to a reference-to-const, hence case_0() is callable from both of the std::function types being used, thus overload resolution is ambiguous when an implicit conversion is used, requiring you to specify the desired std::function type explicitly.
A reference-to-const cannot be assigned to a reference-to-non-const, hence case_1() is not callable from std::function<int(int const&)>, only from std::function<int(int&)>, thus overload resolution is not ambiguous when an implicit conversion is used.
A standalone function is not itself a std::function object, but can be assigned to a compatible std::function object.
Likewise, a lambda is not itself a std::function object, it is an instance of a compiler-defined functor type, which can be assigned to a compatible std::function object.
In both cases, std::function acts as a proxy, passing its own parameters to the function/lambda's parameters, and then returning whatever the function/lambda returns.
So, overload resolution fails for both case_0 and f for the exact same reason. When the compiler has to implicitly convert case_0/f into a std::function object, the conversion is ambiguous because case_0/f is callable from both of the std::function types being used.

GCC but not Clang changes ref-qualifier of function type for a pointer to qualified member function

Following snippet compiles in Clang but not in GCC 12.
// function type (c style)
//typedef int fun_type() const&;
// C++ style
using fun_type = int() const&;
struct S {
fun_type fun;
};
int S::fun() const& {
return 0;
}
int main()
{
fun_type S::* f = &S::fun;
}
Produces error in GCC:
prog.cc: In function 'int main()':
prog.cc:21:25: error: cannot convert 'int (S::*)() const &' to 'int (S::*)() const' in initialization
21 | fun_type S::* f = &S::fun;
| ^~~~~~~
Declaration of S should be equivalent of following declaration
struct S {
int fun() const&;
};
Using this declaration doesn't change behaviour of either compiler. Is this a bug in compiler's translation module related to an under-used feature of language? Which compiler is correct standard-wise?
Which compiler is correct standard-wise?
Clang is correct in accepting the program. The program is well-formed as fun_type S::* f is equivalent to writing:
int (S::*f)() const &
which can be initialized by the initializer &S::fun.

Get a pointer to a templated lambda operator () without captures

Can anyone tell me a valid way to get a pointer to a templated lamda (without caputes) operator () ? Already tried two alternatives:
int main()
{
static
auto l = []<bool a, bool b>( unsigned c ) -> unsigned
{
return (unsigned)a + b + c;
};
using fn_t = unsigned (*)( unsigned );
fn_t fnA = &l.operator ()<true, false>; // doesn't work
fn_t fnB = &decltype(l)::operator ()<true, false>; // also doesn't work
}
clang(-cl) 12:
x.cpp(9,13): error: cannot create a non-constant pointer to member function
fn_t fnA = &l.operator ()<true, false> ; // doesn't work
^~~~~~~~~~~~~~~~~~~~~~~~~~~
x.cpp(10,14): error: address of overloaded function 'operator()' does not match required type
'unsigned int (unsigned int)'
fn_t fnB = &decltype(l)::operator ()<true, false> ; // also doesn't work
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
x.cpp(4,11): note: candidate function template has different qualifiers (expected unqualified but found 'const')
auto l = []<bool a, bool b>(unsigned c) -> unsigned
^
MSVC 2019 latest update:
x.cpp(9): error C2276: '&': illegal operation on bound member function expression
x.cpp(10): error C2440: 'initializing': cannot convert from 'overloaded-function' to 'fn_t'
x.cpp(10): note: None of the functions with this name in scope match the target type
The type of the address of lambda's operator() is a member function pointer, however, the definition of your fn_t is just a free function pointer. You should define your fn_t as:
using fn_t = unsigned int (decltype(l)::*)(unsigned int) const;
Then the following should work:
fn_t fnA = &decltype(l)::operator ()<true, false>;
Or, why not?
auto fnB = &decltype(l)::operator ()<true, false>;
Demo.
It is the lambda which can be converted to function pointer in some conditions (which are not there here), not its member operator(). And you cannot convert member pointer to function pointer.
One workaround is to use another lambda.
fn_t fn = [](unsigned c) { return decltype(l){}.operator()<true, false>(c); };
Demo

What is the correct template parameter/argument for operator[] as a member function?

In an attempt to call a function template which accepts a type and a parameter/argument of that type as the template parameters/arguments, compiler gives an error which is not produced with similar parameters/arguments. So I was wondering what is the correct parameters/arguments in case of calling the function templates for the member function "operator[]const" of a vector class!
Consider this piece of code:
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
The above code is alright now consider the main function:
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
The first call of the function template is OK but not the second one.
The error which compiler produce is:
error: no matching function for call to ‘function<not_OK_type, not_OK_pointer>()’
note: candidate: ‘template<class t__, t__ <anonymous> > void function()’
note: template argument deduction/substitution failed:
error: ‘const int& (std::vector<int>::*)(size_t) const{((const int& (std::vector<int>::*)(size_t) const)std::vector<int>::operator[]), 0}’ is not a valid template argument for type ‘const int& (std::vector<int>::*)(long unsigned int) const’
function<not_OK_type, not_OK_pointer>();
note: it must be a pointer-to-member of the form ‘&X::Y’
Interestingly even if the function template was formed as:
template<auto>
void function(){}
it would cause the same result.
I must add that in the case of non const version, error is the same (for std::vector).
So I am wondering
A: what is wrong?
B: Considering that if there was a mismatch between the not_OK_type and &vector_type::operator[], then compiler would also give an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
Is there a difference between types that can be used as constexpr and the type that can be used as a template parameter/argument?
The issue is typedef const int&(vector_type::* not_OK_type)(size_t)const;.
If you see stl_vector.h (here), the operator[] is declared as noexcept at line 1040.
But in the declaration of not_OK_type variable, noexcept is not present. That's why the compiler complains.
For getting rid of compilation error, add noexcept to not_OK_type variable. Like this:
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
Working code:
#include <vector>
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
#Kunal Puri, provided a correct answer for the the section A of the question.
As for the section B of the question, I guess the clue can be found in one exception that applies when converted constant expression used as template argument for a non-type template parameter.
According to (https://en.cppreference.com/w/cpp/language/constant_expression), a converted constant expression under certain condition and specifically in case of conversion of pointer to noexcept function to pointer to function is a constant expression. Which explains why compiler did not produce an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
However according to (https://en.cppreference.com/w/cpp/language/template_parameters), this type of converted constant expression can not be used as a pointer to non-static data members(and maybe also a pointer to non-static member functions) for a non-type template parameter.
This exception might be the source of conflict between types that can be used as constexpr and as template argument, although statements in the later source are vague and not directly linked to the case.

calling a template method results in no match for 'operator<' [duplicate]

This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 4 years ago.
I have a problem with the following Minimal Example:
enum class modes_t {m1=0u, m2=1u};
class A {
public:
static inline A& get(void) noexcept{
static A inst;
return inst;
}
template<modes_t mode>
inline void start(void) noexcept {}
};
template<typename T>
class B {
public:
static inline void start(void) noexcept {
T::get().start<modes_t::m1>();
}
};
int main() {
B<A>::start();
}
If I compile it with g++ v.7.3, (with -std=c++17 and also with -std=c++14) I get the following errors:
main.cpp: In static member function ‘static void B<T>::start()’:
main.cpp:17:37: error: expected primary-expression before ‘)’ token
T::get().start<modes_t::m1>();
^
main.cpp: In instantiation of ‘static void B<T>::start() [with T = A]’:
main.cpp:22:11: required from here
main.cpp:17:23: error: no match for ‘operator<’ (operand types are ‘<unresolved overloaded function type>’ and ‘modes_t’)
T::get().start<modes_t::m1>();
So the compiler thinks, that the line T::get().start<modes_t::m1>(); is a less then comparison of T::get().start and modes_t::m1.
What should I do, to tell the compiler, that this expression should call start() in class A?
You need to add template keyword to tell the compiler that start is a member function template. e.g.
T::get().template start<modes_t::m1>();
// ~~~~~~~~
BTW: You can't specify modes_t::m1 as the template argument; start expects an int as its template parameter and there are no implicit conversions from the values of a scoped enumerator to integral types.