2

I want to pass a lambda to a function, but I have run into a problem of successfully passing it onto the function. The function chooses to append TrueVal or FalseVal and creates a vector of boolean, based on the given condition.

I'm using 2019 Visual Studio's ISO C++14 Standard to compile the code.

#include <iostream>
#include <vector>

using namespace std;

template<typename T, typename T1, typename T2>
vector<bool> ConstructNestedVectorByElements(T condition, T1 TrueVal, T2 FalseVal) {
    vector<bool> TempCol;
        TempCol = {};
        for (int i = 0; i < 3; i++)
        {
            if (condition(i)) {
                TempCol.emplace_back(TrueVal);
            }
            else {
                TempCol.emplace_back(FalseVal);
            }
        }
    return TempCol;
}

int main()
{
    vector<int> NumList = { 0, 1, 2 };
    vector<bool> BoolList = {true, false, true};

    auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
    vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, false); //this works

    auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
    vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement); //error

    return 0;
};

The error message:

C2440 'initializing': cannot convert from 'T2' to 'bool' ...\include\vector line 2385

1>...\vector(2385,18): error C2440: 'initializing': cannot convert from 'T2' to 'bool'
1>        with
1>        [
1>            T2=main::<lambda_e116e485fb739b952327b9205614af81>
1>        ]
1>...\vector(2385,18): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>...\Source.cpp(19): message : see reference to function template instantiation 'decltype(auto) std::vector<bool,std::allocator<bool>>::emplace_back<T2&>(T2 &)' being compiled
1>        with
1>        [
1>            T2=main::<lambda_e116e485fb739b952327b9205614af81>
1>        ]
1>...\Source.cpp(36): message : see reference to function template instantiation 'std::vector<std::vector<bool,std::allocator<bool>>,std::allocator<std::vector<bool,std::allocator<bool>>>> ConstructNestedVectorByElements<main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,bool,main::<lambda_e116e485fb739b952327b9205614af81>>(T,T1,T2)' being compiled
1>        with
1>        [
1>            T=main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,
1>            T1=bool,
1>            T2=main::<lambda_e116e485fb739b952327b9205614af81>
1>        ]
template <class... _Valty>
    decltype(auto) emplace_back(_Valty&&... _Val) {
        bool _Tmp(_STD forward<_Valty>(_Val)...);
        push_back(_Tmp);

I think the problem might be one of the following:

  • I'm passing more than one type of argument into T2 (a lambda and a bool): Perhaps I used the wrong keyword, typename, to initialize T2? I tried with class but the same thing occurred.
  • OriginalElement isn't given parameters when it requires them: This confuses me a bit. If I change the line to:
TempCol.emplace_back(FalseVal(i, j)); //this is line 19

This error shows up:

C2064 term does not evaluate to a function taking 2 arguments ...\Source.cpp line 19

However, this seems not to be the case for condition(i, j), which compiles correctly. Is there a difference in handling (what I assume to be) boolean when in a conditional, and when appending it to a vector?

  • Lambdas not being constexpr, so it can't be used in templates: I don't really understand it, but there seems to be some relationship with this topic: (1, 2, 3)
  • 1
    Well what do you expect? In the one scenario `T2 == bool` and in the other `T2 == lambda(x)`. But in both cases you try to push that value to a `vector`. – Timo Jul 06 '21 at 07:55
  • 1
    When you call `.emplace_back(.., .., ..)` instead of `..` you pass some values that are used as the respective arguments of some constructor of your `vector`'s type. In your case compiler tries to construct a `bool` (because that's type of element of `TempCol`), and as an argument of constructor your pass a lambda object. There's no such constructor of `bool` – Alexey S. Larionov Jul 06 '21 at 07:56

1 Answers1

1

The issue is that OriginalElement is not a bool and cannot be implicitly converted to one. You can call it to get a bool by passing an int. Change this line in the template:

TempCol.emplace_back(FalseVal(i));

then

auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement);

Compiles fine, but passing false will not work, because false is not a callable. T2 cannot be both, a callable and a bool. If you want both instantiations to work, you can change the first to

auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, [](int){ return false;});

Complete Demo

However, I suggest you to write two overloads. One that takes a callable. And to avoid the overhead of the callable, another one that takes a plain bool (no template parameter, let the conversion happen on the caller).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185