为什么MSVC无法编译这个模板函数?

Why does MSVC fail to compile this template function?

本文关键字:函数 MSVC 编译 为什么      更新时间:2023-10-16

我在将一些代码移植到MSVC时遇到了一个问题,这让我很困惑。据我所知,代码应该是合法的,Clang可以很好地编译它。

我把范围缩小到以下几个:

enum E {
    x
};
template <typename T>
struct traits {
    static const E val = x;
};
template <E e>
struct S {
    S(){};
};
template <typename T>
S<traits<T>::val> foo(T t);
int main() {
    char c = 0;
    foo(c);
}
请注意,在编译之后,代码预计会产生一个链接器错误(我剥离了函数foo的定义以保持示例最小化),但是据我所知,它应该可以清晰地编译

然而,MSVC给了我这个错误:

错误C2893:特化函数模板'S::val> foo(T)'失败

我的问题是:

  • MSVC拒绝代码是否正确?(如果是,为什么?)
  • 如果没有,谁能缩小它做错了什么?也就是说,这是一个他们根本没有实现的语言特性(比如模板的两阶段名称查找),还是"仅仅"是他们声称支持的特性实现中的一个明显错误?

我已经在vc++ 2010和2012上重现了这个问题。

在我自己运行了一些测试之后,这似乎是MSVC中的编译器错误。虽然它在GCC中工作得很好,但当您试图在模板参数中使用traits<T>::val以返回S< E e >时,MSVC会给出神秘且无益的编译器错误(与您问题中的错误相同)。

有趣的是,当您将S< E e >更改为整数时,它可以工作。考虑以下示例,与您的示例相同,只是命名不同:

enum E {
    x
};
template <typename T>
struct traits {
    static const E val = x;
};
template <E e>
struct S {
    S(){};
};
template <typename T>
S< traits<T>::val > tricky(T t) {
    return S< traits<T>::val > ();
};
int main() {
    char thiskidwhowalksaround = 0;
    S<x> s = tricky( thiskidwhowalksaround );
}

现在,让我们只改变一件事:

template <int e> // int instead of E
struct S {
    S(){};
};

程序为我完美地编译(链接和运行)。如果您也恢复到原始状态,然后直接传递E的值,如:

template <typename T>
S< x > tricky(T t) { 
// ^ here
    return S< x > (); // <-- here
};

然后程序编译文件。MSVC在尝试做以下事情时遇到了失败的问题:

traits<T>::val

,其中val是任意类型的枚举。我99%肯定这是编译器本身的缺陷。这似乎是一个结构完美的c++,所以我不能说GCC做错了什么,也不能说原始代码片段正常工作是在扩展。因此,我能收集到的最好的信息是,与同类产品相比,MSVC在编译器健壮性方面有所欠缺。

你可以在这里停止阅读,因为现在我要花一点时间来抱怨MSVC编译器。

begin<rant>这并不是说vc++团队不好或者c++不好,但是从我收集到的微软编译器团队和标准库团队的信息来看——截止到写这篇文章的时候——与其他部门相比,非常小。令我恼火的是,如此基础和重要的语言,以及微软行业核心的一部分,却只有相对较少的人力,以至于它无法跟上——在我短暂的一生中——我发现它是世界上发展最慢的标准之一。我当然不是在批评那些在vc++团队工作的人,但我深感困惑的是,为什么没有更多的人致力于提高c++的速度,而且使编译器工作得更好,和其他产品领域一样好。end<rant>