VS2012 SP1(+十一月包)未知类型错误(类似 C::a(T &&...) )

Posted

技术标签:

【中文标题】VS2012 SP1(+十一月包)未知类型错误(类似 C::a(T &&...) )【英文标题】:VS2012 SP1 (+november pack) unknown-type errors (alike C::a(T &&...) ) 【发布时间】:2013-02-01 14:25:16 【问题描述】:

所以ms compiler online could not compile this(就像我的家 VS2012 和 SP1(+十一月包)一样墙)而clang and modern gcc could。谁能告诉我 VS 中缺少什么 C++11 功能,有什么办法吗?

#include <iostream>
#include <utility>
#include <type_traits>

struct A 
    int x;

    void a() 
        std::cout << "an a! " << x << "\n";
    
;

struct B 
    double x;

    double b(double k) 
        std::cout << "b! " << x << ", " << k << "\n";
        return x - k;
    

    void b() 
        std::cout << "b! " << x << ", ?\n";
    
;

struct C 
    A *_first__;
    B *_second__;
     C(A * _first__, B * _second__):_first__(_first__), _second__(_second__) 
     template < typename K, typename ... T > static auto _a_caller__(K * k, T && ... args)->decltype(k->a(std::forward < T > (args) ...)) 
    return k->a(std::forward < T > (args)...);
    
    template < typename...T > auto a(T &&...args)->decltype(_a_caller__(_first__, std::forward < T > (args)...)) 
        return _a_caller__(_first__, std::forward < T > (args)...);
    
    template < typename...T > auto a(T &&...args)->decltype(_a_caller__(_second__, std::forward < T > (args)...)) 
        return _a_caller__(_second__, std::forward < T > (args)...);
    
    template < typename K, typename...T > static auto _b_caller__(K * k, T && ... args)->decltype(k->b(std::forward < T > (args) ...)) 
        return k->b(std::forward < T > (args)...);
    
    template < typename...T > auto b(T &&...args)->decltype(_b_caller__(_first__, std::forward < T > (args)...)) 
        return _b_caller__(_first__, std::forward < T > (args)...);
    
    template < typename...T > auto b(T &&...args)->decltype(_b_caller__(_second__, std::forward < T > (args)...)) 
        return _b_caller__(_second__, std::forward < T > (args)...);
    
;

int main() 
    A a 12;
    B b 24;

    C c (&a, &b);

    c.a();
    c.b();
    std::cout << c.b(2445) << std::endl;

错误:

testvc.cpp
--\testvc.cpp(38) : error C2535: 'unknown-type C::a(T &&...)' : member function already defined or declared
        --\testvc.cpp(33) : see declaration of 'C::a'
--\testvc.cpp(47) : error C2535: 'unknown-type C::b(T &&...)' : member function already defined or declared
        --\testvc.cpp(42) : see declaration of 'C::b'
--\testvc.cpp(56) : error C2893: Failed to specialize function template 'unknown-type C::a(T &&...)'
        With the following template arguments:
        ''
--\testvc.cpp(57) : error C2893: Failed to specialize function template 'unknown-type C::b(T &&...)'
        With the following template arguments:
        ''
--\testvc.cpp(58) : error C2893: Failed to specialize function template 'unknown-type C::b(T &&...)'
        With the following template arguments:
        'int'

【问题讨论】:

能否将代码和错误信息粘贴到问题中? 顺便说一下,包含__ 的名称是保留的,不应使用。 你应该创建一个SSCCE 我想知道为什么这会在 GCC 和 Clang 上编译:您定义了两对重载(用于 C 中的 a()b()),它们仅在返回值上有所不同 @AndyProwl:这是 SFINAE。在调用c.a() 时,第二个重载无法将_second 替换为_a_caller 的参数,因为没有B::a;所以只有第一个重载被实例化。只有当AB 都有成员a 时,代码才会编译失败。 【参考方案1】:

[此答案已更新。见文末EDIT]

我将此归结为SSCCE:

#include <iostream>

struct A  A g(int)  return A();  ;
struct B  B g()  return B();  ;

struct C

    template<typename... Ts>
    auto f(Ts... ts) -> decltype(A().g(ts...)) 
     std::cout << "f -> A" << std::endl; return A(); 

    template<typename... Ts>
    auto f(Ts... ts) -> decltype(B().g(ts...)) 
     std::cout << "f -> B" << std::endl; return B(); 
;

int main()

    C c;
    c.f(1);

GCC 4.7.2 和 Clang 3.2 编译这个,而 VC11 没有。事实上,当 decltype 表达式内的替换失败时,VC11 似乎不应用 SFINAE,这很可能是一个错误

其实C++11标准规定(14.8.2/7):

替换发生在函数类型和模板参数声明中使用的所有类型和表达式中。表达式不仅包括常量表达式,例如出现在数组边界或作为非类型模板参数的表达式,还包括sizeofdecltype 和其他内部的一般表达式(即非常量表达式)允许非常量表达式的上下文。 [...]

也与 SFINAE 相关的是 14.8.2/8,其中添加了:

如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。 [...]只有无效的类型和表达式在函数类型及其模板参数类型的直接上下文中会导致推导失败。

那么这是“在直接上下文中”替换失败的情况吗?同一段阐明了“直接上下文”的含义:

注意: 替换类型和表达式的求值可能会产生副作用,例如类模板特化和/或函数模板特化的实例化,生成隐式定义的函数等。此类副作用在“直接上下文”中,可能导致程序格式错误。

在我的简化示例中,替换失败肯定发生在直接上下文中,因为它不涉及任何模板实例化或专门化。因此,VC11 肯定包含一个bug

但是,在 您的 示例中,替换是否发生在“直接上下文”中不太明显,因为在 decltype 表达式中,函数模板 (_b_caller__) 实例化是 尝试过

这里的关键观察是实例化已尝试但从未执行,因为 类型推导 失败(同样,由于 decltype 中的表达式的替换失败尝试实例化的模板函数的子句)。因此,该错误不会发生在模板实例化的嵌套上下文中。

因此,这属于 VC11 错误

P.S.:请参阅 this Q&A on SO,了解 SFINAE 不适用的情况,因为替换失败发生在嵌套上下文中。

编辑:

事实证明我的回答是不正确,但我决定保留其原始文本,因为我相信推理并非微不足道,并且可能对某些人有所帮助。但是,我忽略了一个重要方面。

正如Johannes Schaub 在下面的评论中正确指出的那样,上面f() 的第二个定义格式不正确,不需要诊断。这是由 C++11 标准的第 14.6/8 段规定的:

[...] 如果可变参数模板的每个有效特化都需要一个空模板参数包,则模板定义格式不正确,不需要诊断。 [...]

因此,尽管程序格式错误,但编译器不需要(即使它们被允许)发出错误。这意味着无法编译该程序不是 VC11 的不是一个错误,而是一个很好的功能,因为编译器检测到一个不需要检测的错误(尽管必须说该错误消息非常具有误导性)。

【讨论】:

第二个f是“illformed, NDR”,因为它只有在模板参数包为空时才有效。 @JohannesSchaub-litb:做我的语言律师作业:这是由于 14.6/8,它说 “如果可变参数模板的每个有效特化都需要一个空模板参数包,则模板定义格式不正确,不需要诊断。”? 请提交错误报告,让我们看看官方答案。 @myWallJSON:我不能:这不是错误。请参阅我编辑的答案。【参考方案2】:

这很复杂。我猜,GCC 和 CLANG 正在使用 SFINAE 来消除这两个函数模板的歧义,乍一看是模棱两可的。让我们看一个例子:调用c.b(2445) 我会弄乱一些类型和实际参数,但我希望我的意思可以理解。

    实例化第一个函数模板,含义

    auto b&lt;int&gt;(int args)-&gt;decltype(_b_caller__(_first__, std::forward &lt;int&gt; (args))),它反过来实例化_b_caller&lt;A,int&gt;,它调用_first__-&gt;b(int)。由于 A 没有方法 b,因此两个实例化都失败了。这导致

    实例化第二个函数模板,含义

    auto b&lt;int&gt;(int args)-&gt;decltype(_b_caller__(_second__, std::forward &lt;int&gt; (args)))_b_caller&lt;B,int&gt; 有效,因为 B 有一个方法 b(double)。

Visual Studio 似乎在这个过程中的某个地方退出了。我的猜测是,SFINAE 不能正确处理尾随返回类型,但也可能是两级深度实例化使得在这种情况下很难正确应用 SFINAE。

编辑:这可能是相关的: Why does SFINAE not apply to this?

【讨论】:

以上是关于VS2012 SP1(+十一月包)未知类型错误(类似 C::a(T &&...) )的主要内容,如果未能解决你的问题,请参考以下文章

vs2005 SP1发布网站时发布失败,没有错误信息,没有警告信息。怎么解决啊?

vs2008SP1+AutoCAD2010+ObjectARX2010编译出错

视觉 C++ 2010 SP1

调试 XSLT 文件时 VS2008 SP1 崩溃

VS 2003:服务包安装确认

Hive 错误未知类型 TimeStamp 。您是不是忘记注册适配器。如何为外部包中使用的对象类型生成 TypeAdapter?