1

I have a class with a templated member function that takes a callable type as an argument and has a return type deduced from the return type of the supplied function using decltype with the trailing return type syntax. A minimal example is shown below.

#include <iostream>
#include <vector>

class point {
    public:
        std::vector<double> val;
        point(double in) : val({in}) {};
        template<typename T> auto eval(const T& in) -> decltype(in(val[0]));
};

template<typename T>
auto point::eval(const T& in) -> decltype(in(val[0])){
    return in(val[0]);
}

int main(int argc, char *argv[]){

    point pt(2.0);
    auto f = [](double x){return x*x;};

    std::cout << pt.val[0] << std::endl;
    std::cout << pt.eval(f) << std::endl;
}

This code compiles and works as expected in clang++ and g++-5 but chokes with the following error in g++-6.

> g++-6 main.cpp -o test -std=c++11 -Wall main.cpp:13:6: error:
> prototype for 'decltype
> (in(0->((point*)this)->point::val.std::vector<_Tp,
> _Alloc>::operator[]<double, std::allocator<double> >())) point::eval(const T&)' does not match any in class 'point'  auto
> point::eval(const T& in) -> decltype(in(val[0])){
>       ^~~~~ main.cpp:9:35: error: candidate is: template<class T> decltype (in(0->((point*)this)->point::val.std::vector<_Tp,
> _Alloc>::operator[]<double, std::allocator<double> >())) point::eval(const T&)
>          template<typename T> auto eval(const T& in) -> decltype(in(val[0]));
>                                    ^~~~ main.cpp:9:35: error: 'decltype (in(0->((point*)this)->point::val.std::vector<_Tp,
> _Alloc>::operator[]<double, std::allocator<double> >())) point::eval(const T&) [with T = main(int, char**)::<lambda(double)>;
> decltype (in(0->((point*)this)->point::val.std::vector<_Tp,
> _Alloc>::operator[]<double, std::allocator<double> >())) = double]', declared using local type 'const main(int, char**)::<lambda(double)>',
> is used but never defined [-fpermissive] main.cpp:9:35: warning:
> 'decltype (in(0->((point*)this)->point::val.std::vector<_Tp,
> _Alloc>::operator[]<double, std::allocator<double> >())) point::eval(const T&) [with T = main(int, char**)::<lambda(double)>]'
> used but never defined make: *** [all] Error 1

The compiler error does not appear if the implementation is written inline, i.e.

template<typename T> auto eval(const T& in) -> decltype(in(val[0])){
            return in(val[0]);
}

Additionally in c++14 using automatic return type deduction by changing the signature to

template<typename T> auto eval(const T& in);

works as expected.

Is this a gcc compiler bug or is clang incorrectly accepting the above code? What if any is the difference between these function signatures? Why does inlining the implementation make a difference?

kleinhenz
  • 13
  • 4

2 Answers2

1

Yeah, likely a gcc bug. A workaround is to do

template<typename T>
std::result_of<T(double)> point::eval(const T& in) {
A32167
  • 26
  • 2
-2

that is because you are not explicitly instantiating template function definition. As far as I understand, it is not valid c++ code and compilers don't have to accept it. If you want to reproduce this error on other compilers, try moving point class to header file and point::eval definition into .cpp file. So you either have to explicitly instantiate this definition for every type of argument (which is impossible with lambdas), or (as I suggest) don't use out-of-line templates unless urgently required.

Community
  • 1
  • 1
Andrei R.
  • 2,374
  • 1
  • 13
  • 27