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?
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.
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?
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).