TartanLlama / function_ref

A lightweight, non-owning reference to a callable.
Creative Commons Zero v1.0 Universal
169 stars 23 forks source link

Visual C++ compiler behaviour #10

Closed flexorx closed 5 years ago

flexorx commented 5 years ago

In all VC++ up to x64 msvc v19.20 with /std:c++latest, this compiles:

#include <iostream>
#include <vector>
#include <functional>

using T = tl::function_ref<int(const std::vector<int>)>;

void foo (const T& func) {
    std::cout << "Result is " << func({12}); //42
}

int main ()
{

int z = 12;
auto f = [&](const std::vector<int> i) { return i[0]*z; };
foo((T&) (f));

}

But not this:

#include <iostream>
#include <vector>
#include <functional>

using T = tl::function_ref<int(const std::vector<int>)>;

void foo (const T& func) {
    std::cout << "Result is " << func({12}); //42
}

int main ()
{

int z = 12;
auto f = [&](const std::vector<int> i) { return i[0]*z; };
foo(f);
}

Also works if one replaces vector with, say, int.

#include <iostream>
#include <vector>
#include <functional>

using T = tl::function_ref<int(int)>;

void foo (const T& func) {
    std::cout << "Result is " << func({12}); //42
}

int main ()
{

int z = 12;
auto f = [&](int i) { return i*z; };
foo(f);

}
Quuxplusone commented 5 years ago

@flexorx, you do have a couple of unrelated bugs in your usage of function_ref:

(1) You should pass function_ref by value, not by const-reference. (See my various blog posts on pass-by-value for cheap "parameter" types here.)

(2) You should pass vector by value or by const-reference, but never ever by "const value"! See my blog post "Const is a Contract". This goes for both the signature of function_ref<int(const vector<int>)> and the signature of f itself.

However, fixing this issues won't help your MSVC compilation problem here. The problem is related to namespace std itself! https://godbolt.org/z/-acFsP

@TartanLlama, here's the issue: an unqualified ADL call to invoke hidden in your detail namespace.

template <class F, class... Us>
struct invoke_result_impl<
    F, decltype(invoke(std::declval<F>(), std::declval<Us>()...), void()),
    Us...> {
  using type = decltype(invoke(std::declval<F>(), std::declval<Us>()...));
};

Those two instances of invoke should be tl::detail::invoke, not ADL invoke.

In general, when you're writing library code, every unqualified function call is a bug.