Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VC++14 template ambiguity between pointer to member and pointer to member function

Tags:

c++

c++14

I have problem with function templates using vs14 compiler. So the following code demonstrates the issue.

#include <iostream>
using namespace std;

class Class {
public:
    int memberFoo() {
        return 0;
    }
};

template <class VariableT, class C>
void nothing(const VariableT C::*memberV) {
    cout << "Pointer to member variable";
}

template <class R, class C>
void nothing(R (C::*memberF)()) {
    cout << "Pointer to member function";
}

int main() {

    nothing(&Class::memberFoo);

    return 0;
}

Compiler lets me know that nothing function is ambiguous. When I see the output, it seems to have other behaviour than I expected. In the first nothing function, compiler deduces VariableT as int(void). Actually it's not that weird, but I supposed that the second one will be more suitable and will be matched. What is more interesting if you remove const in first overloaded function, the program will compile correctly. Can you suggest me how to deal with this, please?

like image 806
Jonatan Kłosko Avatar asked Dec 14 '15 21:12

Jonatan Kłosko


1 Answers

You can fix it by using some handy type_traits, is_member_function_pointer and is_member_object_pointer:

template <class R, class C>
void nothing(R (C::*memberF)())
{
    std::cout << "Pointer to function" << std::endl;
}

template <class VariableT, class C>
auto nothing(const VariableT C::* memberV)-> typename std::enable_if<std::is_member_object_pointer<decltype(memberV)>::value>::type
{
    cout << "Pointer to member variable";
}

Live Demo

The default type from std::enable_if is void, so the return type remains void.

From your post it sounds like the member function template should be instantiated, but Visual Studio and Clang both yell about ambiguity, which I suppose is fair, given Type Class::*, you could infer a pointer to member or pointer to member function, assuming that a substitution of ReturnType() is valid for Type (which is what we're seeing).

So I decided to skip all that and take advantage of the trailing return type to interrogate the parameter directly.


EDIT

Including text from link in T.C.'s comment

Section: 14.8.2.1 [temp.deduct.call]
It is not clear whether the following is well-formed or not:

void foo(){}

template<class T>
void deduce(const T*) { }

int main() {
  deduce(foo);
}

Implementations vary in their treatment of this example.

Indeed, you noticed that removing const also removed the ambiguity.

like image 57
AndyG Avatar answered Oct 22 '22 00:10

AndyG