0

I am using Clang 14.0.0 and C++20. My goal is to write a general function in order to use std::lerp on a set of elements. So I came up with this:

template <typename InputIterator, typename OutputIterator>
constexpr OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result, 
             std::iter_value_t<InputIterator> const& a, 
             std::iter_value_t<InputIterator> const& b)
{
    return std::transform(first, last, result, [&a, &b] (auto const &t) { return std::lerp(a, b, t); });
}

This works well, however - in an attempt to find a better/alternative way - I thought that maybe a version with std::bind could be more concise and elegant:

template <typename InputIterator, typename OutputIterator>
constexpr
OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result, 
             std::iter_value_t<InputIterator> const& a, 
             std::iter_value_t<InputIterator> const& b)
{
    return std::transform(first, last, result, std::bind(std::lerp(a, b, std::placeholders::_3)));
}

Lastly I found that std::bind_front allows me to be even less verbose:

template <typename InputIterator, typename OutputIterator>
constexpr
OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result, 
             std::iter_value_t<InputIterator> const& a, 
             std::iter_value_t<InputIterator> const& b)
{
    return std::transform(first, last, result, std::bind_front(std::lerp(a, b)));
}

The problem is when I try to compile the two latter versions (the ones that use std::bind and std::bind_front), Clang throws this error message at me:

enter image description here

Here is the calling code:

int main() {
    
    std::vector<double> v = { 0.11, 0.53, 0.32, 0.29, 0.77, 0.45, 0.96, 0.0, 1.0 };
    std::vector<double> results;

    lerp_element(std::begin(v), std::end(v), std::back_inserter(results), 10.0, 20.0);
    for (auto x : results) { std::cout << x << ' '; }
 
    return 0;
}

Godbolt

Since I have the version with the lambda working, it seems the issue must be in the way I am using std::bind. I have been reading everything I could find online on how to properly use std::bind and std::placeholders, but obviously I must not be getting it.

I understand the problem has to do with std::lerp not receiving consistent argument types in order to resolve the overload, so:

  1. why does that happen?
  2. how do I modify the std::bind/std::bind_front versions to make it compile and work as expected?
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Luigi Castelli
  • 676
  • 2
  • 6
  • 13

1 Answers1

3

The function argument to std::bind or std::bind_front should not be invoked, but rather the function itself is passed as the first argument:

using T = std::iter_value_t<InputIterator>;
using F = T (*)(T, T, T) noexcept;

return std::transform(first, last, result,
                      std::bind_front<F>(std::lerp, a, b));

Godbolt

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Yes, that's it. Thanks Patrick. As outlined in the comments, it seems that the lambda solution is the simplest and clearest one in this case. – Luigi Castelli Aug 02 '22 at 07:36
  • 1
    BTW, bind versions are UB, as taking most std function addresses isn't allowed. – Jarod42 Aug 02 '22 at 08:21
  • @Jarod42 thanks for the tip, just found [this](https://stackoverflow.com/q/55687044) after reading your comment. I wasn't familiar with that rule given that most of the code I've written has specifically targeted clang and/or gcc, and both their implementations pretty consistently use addressable functions throughout. – Patrick Roberts Aug 02 '22 at 08:28
  • @PatrickRoberts there are a huge number of different functions denoted by `std::lerp`. how does `std::bind_front`'s argument deduction know which to pick? – Caleth Aug 02 '22 at 11:23