GCC 中的编译器错误,但在将 decltype 与具有尾随返回类型语法的模板化成员函数一起使用时没有 clang

compiler error in gcc but not clang when using decltype with templated member function with trailing return type syntax

本文关键字:成员 语法 函数 一起 clang 返回类型 错误 编译器 GCC decltype      更新时间:2023-10-16

我有一个带有模板化成员函数的类,该函数将可调用类型作为参数,并使用带有尾随返回类型语法的 decltype 从所提供函数的返回类型推导出返回类型。下面显示了一个最小示例。

#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;
}

此代码在 clang++ 和 g++-5 中按预期编译和工作,但在 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

如果实现是内联编写的,则不会出现编译器错误,即

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

此外,在 c++14 中使用自动返回类型推断

,方法是将签名更改为
template<typename T> auto eval(const T& in);

按预期工作。

这是一个 gcc 编译器错误还是 clang 错误地接受了上面的代码?如果这些函数签名之间有什么区别,该怎么办?为什么内联实现会有所不同?

是的,可能是GCC错误。解决方法是执行

template<typename T>
std::result_of<T(double)> point::eval(const T& in) {

这是因为您没有显式实例化模板函数定义。据我了解,它不是有效的 c++ 代码,编译器不必接受它。如果要在其他编译器上重现此错误,请尝试将类point移动到头文件,并将定义point::eval到.cpp文件中。因此,您要么必须为每种类型的参数显式实例化此定义(这对于 lambda 是不可能的),要么(正如我所建议的)除非迫切需要,否则不要使用外联模板。