Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template being deduced as wrong type with template variable

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?

like image 741
RamblingMad Avatar asked Aug 05 '26 19:08

RamblingMad


1 Answers

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

like image 59
Yakk - Adam Nevraumont Avatar answered Aug 07 '26 07:08

Yakk - Adam Nevraumont



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!