Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

class template fails to compile when named lambda is used as template class argument or constructor argument

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.

like image 911
diaz Avatar asked Jun 03 '19 04:06

diaz


People also ask

What are template arguments enlist types of template arguments?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What can the template parameter in C++ template definition be?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

What is CTAD C++?

Alex April 24, 2022, 7:49 pm August 24, 2022. Class template argument deduction (CTAD) C++17. Starting in C++17, when instantiating an object from a class template, the compiler can deduce the template types from the types of the object's initializer (this is called class template argument deduction or CTAD for short).


2 Answers

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;
like image 54
R Sahu Avatar answered Oct 23 '22 14:10

R Sahu


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

like image 2
songyuanyao Avatar answered Oct 23 '22 14:10

songyuanyao