12

我在将一些代码移植到 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 上重现了这个问题。

4

1 回答 1

3

在自己运行了一些测试之后,这似乎是 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++ 不好,而是从我收集到的编译器团队和微软的标准库团队——截至撰写本文时——与其他部门相比微不足道。令我恼火的是,如此基础和重要的语言以及 MS 行业核心的一部分,其人力相对较少,以至于它无法跟上我在短暂的一生中发现的世界上发展最慢的标准之一。我当然不是在抨击在 VC++ 团队中工作的人,但我对为什么没有更多人致力于使 C++ 不仅达到速度,而且使编译器更好地工作并且与其他人一样好,我深感困惑产品领域。 end<rant>

于 2013-03-17T22:23:41.020 回答