8

I am writing a library with many function objects whose classes have several operator() overloads that do not depend on the state of the classes and do not alter it. Now, I tried to make my code work with many old-style APIs (it is not a random need, I actually had to deal with such APIs) and therefore decided to make the function objects convertible to any function pointer corresponding to one of the overloads. At some point, I realized that I had too many such conversions to function pointer operators and that I should theorically be able to write a single variadic conversion operator. Here is a class implementing such a variadic operator:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);

    template<typename... Args>
    operator fptr_t<Args...>() const
    {
        return [](Args... args) {
            // Whatever
        };
    }
};

As you can see, I used the lambda conversion to function pointer to implement the conversion operator, which is not a problem since every function object I have is stateless. The goal was to be able to use the class as follows:

int main()
{
    void(*foo)(int) = foobar();
    void(*bar)(float, double) = foobar();
}

g++ has no problem compiling this code with the expected semantics. However, clang++ rejects it with a template substitution failure error:

main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
    void(*foo)(int) = foobar();
          ^           ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
    operator fptr_t<Args...>() const
    ^
1 error generated.

Note that clang++ has no problem with such conversion operators as long as no variadic templates are involved. If I use a single template parameter, it will have no problem compiling the code. Now, should the code above be accepted or rejected by the compiler?

Morwenn
  • 21,684
  • 12
  • 93
  • 152
  • 4
    I think this the [same bug as this one](https://llvm.org/bugs/show_bug.cgi?id=24032). There is a linked SO question too. Richard Smith [says its a bug](http://stackoverflow.com/questions/31225888/pre-typedefing-a-variadic-function-pointer-argument#comment50471304_31225888) so I believe him :). – Jesse Good Dec 12 '15 at 00:10
  • Workaround: convert with `template operator F*()`, add sfinae test that it is a function type, extract args using traits, unpack using helper, use helper to convert to pointer? – Yakk - Adam Nevraumont Dec 12 '15 at 00:20

1 Answers1

2

A lambda can only be converted to a function pointer if it does not capture, so your code should work. This is justified in the standard 5.1.2/p6 Lambda expressions [expr.prim.lambda] (Emphasis Mine):

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

So I would file it as a CLANG bug.

As a work around for CLANG, you can convert it to a std::function as shown below:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);

    template<typename... Args>
    operator std::function<void(Args...)>() const
    {
        return [](Args... args) {
            //...
        };
    }
};

int main()
{
    std::function<void(int)> f1 = foobar();
    std::function<void(double, float)> f2 = foobar();
    f1(1);
    f2(2.0, 1.0f);
}

Live Demo

101010
  • 41,839
  • 11
  • 94
  • 168
  • Well yeah, I know all of that. I mean, I'm not asking for a solution to my problem (I have worked around it for a while already). I'm only asking for a language-lawyer answer to know to which compiler I shall submit a bug report :p – Morwenn Dec 11 '15 at 23:57
  • @Morwenn you'll file a bug report to CLANG :P. – 101010 Dec 12 '15 at 00:14