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

jal*_*alf 12 c++ templates visual-c++

我遇到了一些问题,将一些代码移植到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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,在编译之后,代码会产生链接器错误(我删除了函数的定义foo以保持样本最小),但据我所知它应该干净地编译.

但是,MSVC给了我这个错误:

错误C2893:无法专门化函数模板'S :: val> foo(T)'

所以我的问题:

  • MSVC是否有机会正确拒绝代码?(如果是的话,为什么?)
  • 如果没有,任何人都可以缩小它的错误吗?如何,它是一个他们根本没有实现的语言功能(例如模板的两阶段名称查找),还是"只是"他们声称支持的功能实现中的一个普通错误?

我在VC++ 2010和2012上重现了这个问题.

小智 3

经过我自己的一些测试,这似乎是 MSVC 中的编译器错误。traits<T>::val虽然它与 GCC 配合得很好,但当您尝试在模板参数内部使用返回值时,MSVC 会给出神秘且无用的编译器错误(与您问题中的错误相同)S< E e >。

有趣的是,当你改为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 );
}
Run Code Online (Sandbox Code Playgroud)

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

template <int e> // int instead of E
struct S {
    S(){};
};
Run Code Online (Sandbox Code Playgroud)

然后该程序会为我完美地编译(链接和运行)。如果你也恢复成原来的,然后E直接传入一个值,比如:

template <typename T>
S< x > tricky(T t) { 
// ^ here
    return S< x > (); // <-- here
};
Run Code Online (Sandbox Code Playgroud)

然后程序编译文件。MSVC 在尝试执行以下操作时遇到了问题:

traits<T>::val

哪里val有任何类型的枚举。我 99% 确定这是编译器本身的缺陷。这似乎是完全格式良好的 C++,所以我不能说 GCC 通过让原始代码片段工作而做了错误的事情或扩展。因此,我能收集到的最好信息是,与同类产品相比,MSVC 再次缺乏编译器的稳健性。

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

begin<rant> 这并不是说 VC++ 团队不好或者 C++ 不好,而是从我收集到的情况来看,截至撰写本文时,微软的编译器团队和标准库团队与其他部门相比规模很小。令我恼火的是,这样一种基础而重要的语言,也是微软行业核心的一部分,其人力相对较少,以至于在我短暂的一生中,它无法跟上我发现的世界上发展最慢的标准之一。我当然不是在批评 VC++ 团队的工作人员,但我深感困惑的是,为什么没有更多的人致力于让 C++ 不仅跟上速度,而且让编译器工作得更好,与其他编译器一样好产品领域。 end<rant>