4

I have some code that compiles using msvc but not under gcc. I would like to know if this is a non standard feature by msvc or a bug in gcc (Or more correctly the version v5.0.3 of MinGW)

Consider the following code:

template <class T>
struct object_with_func_ptr {
    const T func; // An object to hold a const function pointer.
};

class foo {
public:
    void bar() {} // The function I want to point to.

    static constexpr auto get_bar() {
        return object_with_func_ptr<decltype(&bar)>{&bar}; // A constexpr to wrap a function pointer.
    }
};

template <class Func, Func Fn> struct template_using_func {};

int main() {
    constexpr auto bar_obj = foo::get_bar();
    // Note that this is a constexpr variable and compiles for gcc also if the template_using_func line is left out.
    constexpr auto bar_func_ptr = bar_obj.func;
    template_using_func<decltype(bar_func_ptr), bar_func_ptr>();
    return 0;
}

In case this is a non-standard feature of msvc it would be great to know if there are other ways to achieve what I'm aiming to do.

EDIT: Here the compiler error generated by MinGW:

E:\CLion\ErrorExample\main.cpp: In function 'int main()':
E:\CLion\ErrorExample\main.cpp:21:61: error: 'bar_func_ptr' is not a valid template argument for type 'void (foo::* const)()'
template_using_func<decltype(bar_func_ptr), bar_func_ptr>();
                                                        ^
E:\CLion\ErrorExample\main.cpp:21:61: error: it must be a pointer-to-member of the form '&X::Y'
E:\CLion\ErrorExample\main.cpp:21:61: error: could not convert template argument 'bar_func_ptr' from 'void (foo::* const)()' to 'void (foo::* const)()'

EDIT 2: Changing &bar to &foo::bar apparently makes this compile for clang as well (as noted in the comments.), so I'm currently assuming this to be a bug in GCC.

Chartas
  • 335
  • 2
  • 9
  • 2
    Can you post the compile error? – 0x5453 Jan 08 '18 at 19:42
  • 1
    To obtain a pointer to member function you need to write `&foo::bar`. – user7860670 Jan 08 '18 at 19:47
  • @0x5453 I edited my question to show the compiler error. – Chartas Jan 08 '18 at 19:50
  • 1
    error also from clang++ – max66 Jan 08 '18 at 19:50
  • @VTT In general yes, but in this case no because I'm taking the pointer within the class itself. This is not the problem here and changes nothing even if I do change it. – Chartas Jan 08 '18 at 19:51
  • 1
    Recommendation: Rather than saying "most recent", give the explicit g++ version number. Most recent changes over time and because mingw is heavily fragmented, one distribution's most recent could give g++ 4.9.2 and another distribution 's most recent could provide g++ 8. – user4581301 Jan 08 '18 at 19:51
  • 4
    @C.Esch Actually it is mandatory even within class. [Getting pointer to member properly fixes compilation in clang](https://wandbox.org/permlink/6mVs91p95wcVLZa7). Not sure what gcc complains about though. Maybe it has something to do with const adjustments. – user7860670 Jan 08 '18 at 19:52
  • @VTT Hmmm very interesting indeed. It doesn't work for MinGW though. So it's probably okay to assume this is a bug? – Chartas Jan 08 '18 at 19:54
  • 1
    Probably, at least `'void (foo::* const)()' to 'void (foo::* const)()'` conversion failure looks very suspicious. – user7860670 Jan 08 '18 at 19:59

2 Answers2

6

Before C++17, the standard restricts non-null pointer-to-member template arguments to "a pointer to member expressed as described in [expr.unary.op]". In other words, such an argument must be expressed in the exact form &A::B.

C++17 relaxes this constraint to permit general constant expressions. It appears that this has not yet been implemented in GCC.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • According to gcc documentation, N4268 is fully implemented in 6. So this seems to have been missed. – rustyx Jan 08 '18 at 22:45
  • 1
    @RusttyX a gcc N4268 related bug has already been filed, see [this post](https://stackoverflow.com/questions/47606810/can-i-use-the-result-of-a-c17-captureless-lambda-constexpr-conversion-operator) – Massimiliano Janes Jan 09 '18 at 11:03
0

Well there are some obvious errors, like &bar instead of &foo::bar. If we fix those, Clang agrees with MSVC, and when 2 out of 3 major compilers agree and it makes sense, I go with the sense-making majority.

The error when I test this on gcc is void (foo::* const)()' to 'void (foo::* const)() which makes me think this is a bug. Now, it could be some obscure requiremet on non-type template arguments coming from certain value categories that is not being satisfied here, but I'd expect a clearer error message then.

As an aside, std::integral_constant can do a better job of representing a compile-time function pointer than your class, unless you really want the runtime ability to chang where the pointer points.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524