Compile error with libc++'s bind

Hello,

the following program fails to compile with clang trunk and libc++ trunk (note the implicit conversion from <const char*>
to <std::string> when calling the functor in <doIt>):

#include <functional>

using namespace std::placeholders;

template <typename Functor>
static void doIt(Functor f)
{
  f("");
}

static void method(const std::string&); // A
//static void method(const char*); // B

int main(int, char**)
{
  doIt(std::bind(&method, _1));
}

This results in the following error:

$ ~/LLVM/build/Release+Asserts/bin/clang -std=c++0x -stdlib=libc++ clang.cpp
In file included from clang.cpp:1:
/usr/include/c++/v1/functional:1620:8: error: no type named 'type' in 'std::__1::__invoke_of<void
      (*&)(const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>
      > &), char const (&)[1]>'
    >::type type;

Commenting line (A) and uncommenting line (B), i.e. removing the implicit conversion, makes the code compile.

I looked into the standard, section 20.8, but was unable to find anything about implicit conversions, although it seems to
me from reading 20.8.2p1 that they should be allowed.

A similar code used to compile with an older version of libc++ (most likely before Howard's implementations in r131639ff.

So my question is: Is this a bug in libc++, or are implicit conversions not allowed, and the similar code used to compile
because of an implementation artefact that was removed during r131639ff?

With many thanks in advance,
Jonathan

This looks like a bug either in the new __invoke_of trait, or in clang's SFINAE / overload handling. Not sure yet. Investigating...

Howard

Hello,

the following program fails to compile with clang trunk and libc++ trunk (note the implicit conversion from <const char*>
to <std::string> when calling the functor in <doIt>):

#include <functional>

using namespace std::placeholders;

template <typename Functor>
static void doIt(Functor f)
{
f("");
}

static void method(const std::string&); // A
//static void method(const char*); // B

int main(int, char**)
{
doIt(std::bind(&method, _1));
}

This results in the following error:

$ ~/LLVM/build/Release+Asserts/bin/clang -std=c++0x -stdlib=libc++ clang.cpp
In file included from clang.cpp:1:
/usr/include/c++/v1/functional:1620:8: error: no type named 'type' in 'std::__1::__invoke_of<void
    (*&)(const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>

&), char const (&)[1]>'
::type type;

Commenting line (A) and uncommenting line (B), i.e. removing the implicit conversion, makes the code compile.

I looked into the standard, section 20.8, but was unable to find anything about implicit conversions, although it seems to
me from reading 20.8.2p1 that they should be allowed.

A similar code used to compile with an older version of libc++ (most likely before Howard's implementations in r131639ff.

So my question is: Is this a bug in libc++, or are implicit conversions not allowed, and the similar code used to compile
because of an implementation artefact that was removed during r131639ff?

This looks like a bug either in the new __invoke_of trait, or in clang's SFINAE / overload handling. Not sure yet. Investigating...

At the very least we have a misleading error message:

error: no type named 'type' in 'std::__1::__invoke_of<void (*&)(const std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::allocator<char> > &), char const (&)[1]>'
    >::type type;
    ~~~^~~~

And yet:

#include <type_traits>
#include <string>

static void method(const std::string&) {} // A

#include <iostream>

int main()
{
    std::cout << std::__invokable<void (*&)(const std::string&), char const (&)[1]>::value << '\n';
    std::cout << typeid(std::__invoke_of<void (*&)(const std::string&), char const (&)[1]>::type).name() << '\n';
}

outputs:

1
v

Meaning std::__invoke_of<void (*&)(const std::string &), char const (&)[1]> >::type exists and has type void.

Howard

I've submitted this bugzilla:

http://llvm.org/bugs/show_bug.cgi?id=9975

Howard

Hello,

I've submitted this bugzilla:

http://llvm.org/bugs/show_bug.cgi?id=9975

Well, that wasn't the first PR that was the result of my stupidity :-/ Thank you, Richard, for pointing
out the (obvious) mistake in my code! And thank you, Howard, for your patience!

But I still have my original problem with std::bind, the one that lead me to post the incorrect code here.
I managed to reduce it to the following:

#include <functional>

struct Container {
    void lookup() const; // 1
};

void lookup(Container&); // 2

template <typename Functor>
static void foo(const Functor& f) // A
//static void foo(Functor f) // B
{
    f();
}

int main(int, char**)
{
    Container c;
    foo(std::bind(&Container::lookup, std::ref(c))); // C
    //foo(std::bind(&Container::lookup, c)); // D
    //foo(std::bind(&lookup, std::ref(c))); // E
}

This fails to compile with the combination (A)+(C) (functor is passed as a const reference, and std::ref is used
to avoid copying <c> into the functor), but compiles with all other combinations (well, modulo PR9983). It seems
that for some reason using std::ref requires the functor to be non-const, at least when a member function is bound
(the code compiles with the free function "lookup" (1), but not the member function "lookup" (2)).

(My clang and libc++ are both the current trunk.)

Am I (again) overlooking something?

Thanks in advance,
Jonathan

Nope, not this time! You found a bug in libc++, thanks! In [func.bind.bind]/p10/b1 I was detecting when Tid is a reference_wrapper<T>. But when the bind functor is const, I add that cv-qualifier to the bound args, and was not making the proper substitution when Tid is const reference_wrapper<T>.

Checked in fix revision 131852.

Thanks for following through with this!

Howard