3

So I have the following test code:

struct empty_value{
    template<typename T>
    T as(){ return T(0); }
};

template<typename T, typename U, typename F>
auto empty_func(empty_value lhs, empty_value rhs, F f) -> decltype(f(lhs.as<T>(), rhs.as<U>())){
    return f(lhs.as<T>(), rhs.as<U>());
}

template<typename T, typename U, template<typename, typename> class F>
static auto bind_empty_f = std::bind(empty_func<T, U, F<T, U>>, std::placeholders::_1, std::placeholders::_2, F<T, U>{});

template<typename F>
void other_test_func(F&&){}

template<typename T, typename U, template<typename, typename> class F>
void test_func(){
    other_test_func(bind_empty_f<T, U, F>);
}

template<typename T, typename U>
struct my_add{
    decltype(auto) operator()(T lhs, U rhs){ return lhs + rhs; }
};

int main(){
    test_func<float, int, my_add>();
}

Which is derived from something I was actually working on. The problem arises on the line of bind_empty_f. But only when it is passed to other_test_func. When I try to assign it to a regular variable like this:

int main(){
    auto var = bind_empty_f<float, int, my_add>;
}

Everything is all cheery. But if I call test_func which tries to pass it to other_test_func I get an error that the underlying type returned by std::bind can't be converted to float. So it is trying to convert it to the return value of the actual function. I can't understand why. Where am I passing the function's return value?


EDIT

if I call the function after setting a local variable to the value of bind_empty_f first it compiles:

int main(){
    auto var = bind_empty_f<float, int, my_add>;
    test_func<float, int, my_add>;
}

So the issue must be to do with static initialization a compiler bug.

EDIT2

As stated in the comments, this exact example compiles with other compilers but does not with the original tested compiler (GCC 5.2.0).

This is a bug in either GCC 5.2 or every other compiler tested.

So I guess the question becomes, is this standard conforming code?

Community
  • 1
  • 1
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • clang++ and g++6 compile your code – Piotr Skotnicki Sep 09 '15 at 17:26
  • @PiotrSkotnicki [coliru doesn't](http://coliru.stacked-crooked.com/a/7c991cbc59cc1455) – RamblingMad Sep 09 '15 at 17:41
  • @PiotrSkotnicki coliru's version of g++ reports 5.2.0, so it must be a compiler bug – RamblingMad Sep 09 '15 at 17:48
  • @CoffeeandCode: I confirm, it compiles with clang 3.7.0, see http://coliru.stacked-crooked.com/a/dc89de32dd59ef90 – Matthieu M. Sep 09 '15 at 17:51
  • 1
    I'm voting to close this question as off-topic because it appears to be a bug in the development version of gcc. It would make an interesting test for their test suite, but is unlikely to help anyone else. – Matthieu M. Sep 09 '15 at 17:52
  • No, you cannot conclude that yet. Please provide the exact compiler and version, together with the compiler error messages, and the lines they refer to. There are a few errors above (your type deduction of `F&&f`'s call is wrong), and your use of `std::bind` is usually a bad sign (it has quirky self-interaction). I also see what could be some rvalue category errors, which depending on how rvalue aware `bind` is could give you some issues. `bind_empty_f` only accepts an rvalue as the 3rd argument, which should only be passed if invoked in an rvalue context. – Yakk - Adam Nevraumont Sep 09 '15 at 17:54
  • @MatthieuM. fair enough vote – RamblingMad Sep 09 '15 at 17:55
  • @Yakk those aren't rvalue references, they're universal references. Though they could be deduced as rvalue references (they aren't in this case). It is definitely a compiler error. P.S. why do you say my type deduction of `f`s call is wrong? – RamblingMad Sep 09 '15 at 17:57
  • 1
    @CoffeeandCode You may intend them to be universal references, but you are incorrect. `empty_func>` passes `F` as the 3rd type argument, which forces the 3rd function argument to be `F &&f` exactly. Forwarding references (aka universal references) only work "simply" in a type deduction context; you are not doing such type deduction. – Yakk - Adam Nevraumont Sep 09 '15 at 17:59
  • @Yakk: I have not concluded anything; I believe it is a bug, based on the fact that other versions of gcc compile the code as intended and clang also does. Of course, those other compilers/versions could be wrong and only the newest gcc be correct; I find it unlikely, but hey, I'm wrong often enough. – Matthieu M. Sep 09 '15 at 18:00
  • @Yakk ah, yes. I didn't think about the fact it was explicitly declared, I'll change it to an lvalue (as to not arise a different error). It still wouldn't cause the error I am getting though. – RamblingMad Sep 09 '15 at 18:00
  • @MatthieuM. I'm just saying that "one compiler builds something, others do not" is weak evidence that one of those compilers has a bug. (The code may also be ill formed with no diagnostic required, in which case no compiler has a bug). Closing it simply because it works in some compilers is premature; especially when messing around with tangled code `std::bind`s. I will admit the question is far from a simple, minimal example, but there is some hope we can clean it up and find out what is actually going on. – Yakk - Adam Nevraumont Sep 09 '15 at 18:02
  • 1
    @Yakk: You might think of it as a gut feeling; g++-5.1 also accepts the code (http://coliru.stacked-crooked.com/a/bac7fd419b6d6332), which makes me think that the latest release broke something. As I said I may be wrong, of course, in which case I'll be happy to retract my vote or vote to re-open as appropriate. – Matthieu M. Sep 09 '15 at 18:09

1 Answers1

3

Here is a minimal example of your problem:

template<class T> struct tag {};

template<typename T>
static auto bind_empty_f = tag<T>{};

template<typename T>
decltype(bind_empty_f<T>) test_func(){
  return 3.14f;
}

then we simply test_func<float>() and it returns 3.14f. If we test_func<int>() it returns 3.

If we first do a bind_empty_f<float>, instead test_func<float> generates an error.

The type deduced for bind_empty_f<T> when it is called within another template is set to T and not to the type of the right hand side of the expression.

If you call it directly, and the type hasn't already been calculated (there seems to be a cache), then the correct type is deduced, and my test_func fails to build (as it tries to convert 3.14f to a bind expression type and fails).

This is definitely a problem with the compiler. You can work around it by replacing the auto in bind_empty_f with a std::decay_t<decltype(stuff_on_rhs)>.

Note that there are other problems with some of your bind expressions, but they are not central to this issue.

live example compiling (wrongly), live example not compiling (correctly).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524