VS2012 SP1(+十一月包)未知类型的错误(都Ç::一个(T && ...))

2019-10-17 16:03发布

所以MS编译器在线无法编译这个 (如墙为我的家VS2012 SP1的(+十一月包)),而铛和现代的gcc可能 。 可以aany一个高大高兴我什么C ++ 11功能是在VS失踪,有没有办法左右?

#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'

Answer 1:

[This answer has been updated. See the EDIT at the end of the text]

I brought this down to an 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 and Clang 3.2 compile this, while VC11 does not. Indeed it seems VC11 does not apply SFINAE when substitution fails inside the decltype expression, and this is most likely a bug.

In fact, the C++11 Standard specifies (14.8.2/7):

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. [...]

Also relevant to SFINAE is 14.8.2/8, which adds:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.

So is this a case of substitution failure "in an immediate context"? The same paragraph clarifies what is meant by "immediate context":

Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.

In the case of my simplified example, the substitution failure definitely occurs in an immediate context, as it involves no template instantiation or specialization whatsovever. Thus, VC11 definitely contains a bug.

However, it is less obvious whether in your example the substitution happens in an "immediate context", because inside the decltype expression a function template (_b_caller__) instantiation is attempted.

The key observation here is that the instantiation is attempted but never performed, because type deduction fails (again, due to substitution failure for the expression in the decltype clause of the template function whose instantiation is attempted). Thus, the error does not occur in the nested context of a template instantiation.

Hence, this qualifies as a VC11 bug.

P.S.: See this Q&A on SO for a situation where SFINAE does not apply because substitution failure occurs in a nested context.

EDIT:

It turns out my answer was incorrect, but I decided to keep its original text because I believe the reasoning is non-trivial and might be helpful to some. However, I overlooked one important aspect.

As Johannes Schaub correctly pointed out in a comment below, the second definition of f() above is ill-formed and no diagnostic is required. This is dictated by Paragraph 14.6/8 of the C++11 Standard:

[...] If every valid specialization of a variadic template requires an empty template parameter pack, the template definition is ill-formed, no diagnostic required. [...]

Thus, although the program is ill-formed, compilers are not required (even though they are allowed) to issue an error. This means that failure to compile this program is NOT a bug of VC11, but rather a good feature, in that the compiler detects an error it is not required to detect (although it must be said that the error message is quite misleading).



Answer 2:

这是复杂的。 我想,GCC和CLANG使用SFINAE消除歧义两个函数模板,乍一看是ambiguos。 让我们来看一个例子:呼叫cb(2445)我要搞乱一个位类型和实际参数,但我希望这是可以理解我的意思。

  1. 实例化的第一个函数模板,这意味着

    auto b<int>(int args)->decltype(_b_caller__(_first__, std::forward <int> (args)))这反过来又实例_b_caller<A,int> ,它要求_first__->b(int) 。 由于A没有方法B,两实例失败。 这导致

  2. 实例化的第二函数模板,这意味着

    auto b<int>(int args)->decltype(_b_caller__(_second__, std::forward <int> (args)))_b_caller<B,int>其中工程中,由于B具有方法B(双)。

看来在这个过程中Visual Studio中somewehere捞出。 我的猜测是,SFINAE不尾随返回类型正常工作,但它很可能会成为二级深实例化,使得它很难在这种情况下正确应用SFINAE。

编辑:这可能是相关的: 为什么SFINAE均不适用于本?



文章来源: VS2012 SP1 (+november pack) unknown-type errors (alike C::a(T &&…) )