5

I'm currently experimenting with class template programming and I came across this weird behavior that I cant understand when passing a named lambda as its argument. Could somebody explain why (1) & (2) below does not work?

template<typename Predicate>
class Test{
public:
    Test(Predicate p) : _pred(p) {}
private:
    Predicate _pred;
};

int main(){
    auto isEven = [](const auto& x){ return x%2 == 0; };

    // Working cases
    Test([](const auto& x){ return x%2 == 0; });
    Test{isEven};
    auto testObject = Test(isEven);

    // Compilation Error cases
    Test(isEven); // (1) Why??? Most vexing parse? not assigned to a variable? I cant understand why this fails to compile.
    Test<decltype(isEven)>(isEven); // (2) Basically same as (1) but with a workaround. I'm using c++17 features, so I expect automatic class parameter type deduction via its arguments

    return 0;
};

Compiler Error message: Same for (1) & (2)

cpp/test_zone/main.cpp: In function ‘int main()’:
cpp/test_zone/main.cpp:672:16: error: class template argument deduction failed:
     Test(isEven);
                ^
cpp/test_zone/main.cpp:672:16: error: no matching function for call to ‘Test()’
cpp/test_zone/main.cpp:623:5: note: candidate: template<class Predicate> Test(Predicate)-> Test<Predicate>
     Test(Predicate p): _p(p){
     ^~~~
cpp/test_zone/main.cpp:623:5: note:   template argument deduction/substitution failed:
cpp/test_zone/main.cpp:672:16: note:   candidate expects 1 argument, 0 provided
     Test(isEven);
                ^

Please forgive my formatting, and compile error message snippet as it does not match exact lines. I'm using g++ 7.4.0, and compiling with c++17 features.

max66
  • 65,235
  • 10
  • 71
  • 111
diaz
  • 51
  • 1
  • 1
    clang tells that `cannot use parentheses when declaring variable with deduced class template specialization type` about (1). UPD Adding parentheses as songyuanyao's said helps, lol – J. S. Jun 03 '19 at 04:18

2 Answers2

4

In C++, you can declare a variable as

int(i);

which is the same as

int i;

In your case, the lines

Test(isEven);
Test<decltype(isEven)>(isEven);

are compiled as though you are declaring the variable isEven. I am surprised that the error message from your compiler is so different than what I hoped to see.

You can reproduce the problem with a simple class too.

class Test{
   public:
      Test(int i) : _i(i) {}
   private:
      int _i;
};

int main(){

   int i = 10;

   Test(i);
   return 0;
};

Error from my compiler, g++ 7.4.0:

$ g++ -std=c++17 -Wall    socc.cc   -o socc
socc.cc: In function ‘int main()’:
socc.cc:15:11: error: conflicting declaration ‘Test i’
     Test(i);
           ^
socc.cc:10:9: note: previous declaration as ‘int i’
     int i = 10;
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

As you said, this is a most vexing parse issue; Test(isEven); is trying to redefine a variable with name isEven, and same for Test<decltype(isEven)>(isEven);.

As you showed, you can use {} instead of (), this is the best solution since C++11; or you can add additional parentheses (to make it a function-style cast).

(Test(isEven));
(Test<decltype(isEven)>(isEven)); 

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405