So ms compiler online could not compile this (as wall as my home VS2012 with SP1 (+november pack)) while clang and modern gcc could. Can aany one please tall me what C++11 feature is missing in VS and is there ways around?
#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;
}
Errors:
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'
[This answer has been updated. See the EDIT at the end of the text]
I brought this down to an SSCCE:
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):
Also relevant to SFINAE is 14.8.2/8, which adds:
So is this a case of substitution failure "in an immediate context"? The same paragraph clarifies what is meant by "immediate context":
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: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).
That's complicated. I guess, GCC and CLANG are using SFINAE to disambiguate the two function templates, that at first glance are ambiguos. Lets look at an example: The call
c.b(2445)
I'm gonna mess up a bit the types and actual arguments, but I hope it's understandable what I mean.Instantiating the first function template, meaning
auto b<int>(int args)->decltype(_b_caller__(_first__, std::forward <int> (args)))
, which in turn instantiates_b_caller<A,int>
, which calls_first__->b(int)
. Since A has no method b, both instantiations fail. This leads toInstantiating the second function template, meaning
auto b<int>(int args)->decltype(_b_caller__(_second__, std::forward <int> (args)))
and_b_caller<B,int>
which works, since B has a method b(double).It seems that somewehere in that process Visual Studio bails out. My guess is, that SFINAE does not work correctly with trailing return types, but it might as well be the two level deep instantiation that makes it difficult to apply SFINAE correctly in that case.
Edit: this might be related: Why does SFINAE not apply to this?