C++11 decltype/SFINAE puzzler

I'm running into a usability problem with the extended SFINAE rules that could effect best practice for all new C++ libraries. I'm hoping some clever person in the Boost community can help me. I find that in highly generic code, using decltype to define the return type of functions is very handy. Dave A. suggested a RETURNS macro to simplify this idiom: <https://groups.google.com/d/msg/boost-devel-archive/OzJ5Ft3pSnU/b_Ter9bgNqAJ> It's used like this: template<typename T> auto foo( T && t ) RETURNS( some-expression-using-t ); This is great and I use it a lot. Now, imagine you have a library of such functions calling other such functions: function A calls B calls ... calls Y, calls Z, all of which use decltype to declare their return types. Now, the user calls A passing a variable t for which the expression in Z's return type calculation makes no sense. Extended SFINAE kicks in, and Z simply disappears. Since Z has disappeared, the expression in Y's return type makes no sense, so Y disappears, too. And so on up the chain. The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem! So, my question is: what can be done about this? Can we get the benefit of automatic type deduction in return types without causing SFINAE to drop functions from the overload set? Is there another trick we can use to report errors meaningfully when APIs are misused? The only solution I can think of is turn all the free functions in the chain into function objects and then using std::result_of to compute return types for them instead of using decltype directly. That means Dave's (very convenient!) RETURNS macro shouldn't be used, which is a bummer. (I think it also means that we need a noexcept_of template that computes the noexcept of an expression without causing the expression to appear in the function declaration, but that depends on how <http://llvm.org/bugs/show_bug.cgi?id=13286> gets resolved.) All suggestions welcome, -- Eric Niebler BoostPro Computing http://www.boostpro.com

On Fri, 6 Jul 2012, Eric Niebler wrote:
I'm running into a usability problem with the extended SFINAE rules that could effect best practice for all new C++ libraries. I'm hoping some clever person in the Boost community can help me.
I find that in highly generic code, using decltype to define the return type of functions is very handy. Dave A. suggested a RETURNS macro to simplify this idiom:
<https://groups.google.com/d/msg/boost-devel-archive/OzJ5Ft3pSnU/b_Ter9bgNqAJ>
It's used like this:
template<typename T> auto foo( T && t ) RETURNS( some-expression-using-t );
This is great and I use it a lot. Now, imagine you have a library of such functions calling other such functions: function A calls B calls ... calls Y, calls Z, all of which use decltype to declare their return types. Now, the user calls A passing a variable t for which the expression in Z's return type calculation makes no sense. Extended SFINAE kicks in, and Z simply disappears. Since Z has disappeared, the expression in Y's return type makes no sense, so Y disappears, too. And so on up the chain.
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();} template<class T> auto g(T x)->decltype(h(x)){return h(x);} template<class T> auto f(T x)->decltype(g(x)){return g(x);} int main(){ f(3); } e.cc: In function 'int main()': e.cc:5:6: error: no matching function for call to 'f(int)' f(3); ^ e.cc:5:6: note: candidate is: e.cc:3:24: note: template<class T> decltype (g(x)) f(T) template<class T> auto f(T x)->decltype(g(x)){return g(x);} ^ e.cc:3:24: note: template argument deduction/substitution failed: e.cc: In substitution of 'template<class T> decltype (g(x)) f(T) [with T = int]': e.cc:5:6: required from here e.cc:3:24: error: no matching function for call to 'g(int&)' e.cc:3:24: note: candidate is: e.cc:2:24: note: template<class T> decltype (h(x)) g(T) template<class T> auto g(T x)->decltype(h(x)){return h(x);} ^ e.cc:2:24: note: template argument deduction/substitution failed: e.cc: In substitution of 'template<class T> decltype (h(x)) g(T) [with T = int]': e.cc:3:24: required by substitution of 'template<class T> decltype (g(x)) f(T) [with T = int]' e.cc:5:6: required from here e.cc:2:24: error: no matching function for call to 'h(int&)' e.cc:2:24: note: candidate is: e.cc:1:24: note: template<class T> decltype (x.smurf()) h(T) template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();} ^ e.cc:1:24: note: template argument deduction/substitution failed: e.cc: In substitution of 'template<class T> decltype (x.smurf()) h(T) [with T = int]': e.cc:2:24: required by substitution of 'template<class T> decltype (h(x)) g(T) [with T = int]' e.cc:3:24: required by substitution of 'template<class T> decltype (g(x)) f(T) [with T = int]' e.cc:5:6: required from here e.cc:1:24: error: request for member 'smurf' in 'x', which is of non-class type 'int' -- Marc Glisse

Use g++.
template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();} template<class T> auto g(T x)->decltype(h(x)){return h(x);} template<class T> auto f(T x)->decltype(g(x)){return g(x);}
Apparently I should have updated Gmail before hitting send. Looks very familiar :)

On 7/6/2012 12:54 PM, Marc Glisse wrote:
On Fri, 6 Jul 2012, Eric Niebler wrote:
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. <snip informative backtrace>
Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll follow up with them. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <eric@boostpro.com> wrote:
On 7/6/2012 12:54 PM, Marc Glisse wrote:
On Fri, 6 Jul 2012, Eric Niebler wrote:
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. <snip informative backtrace>
Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll follow up with them.
FWIW, top-of-tree Clang produces: t.cpp:12:3: error: no matching function for call to 'f' f(0); ^ t.cpp:8:8: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~ Which could probably still be improved, although I can't convince myself that GCC 4.8 is doing us favors by producing the complete SFINAE backtrace. - Doug

From: doug.gregor@gmail.com On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <eric@boostpro.com> wrote:
On 7/6/2012 12:54 PM, Marc Glisse wrote:
On Fri, 6 Jul 2012, Eric Niebler wrote:
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. <snip informative backtrace>
Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll follow up with them.
FWIW, top-of-tree Clang produces:
t.cpp:12:3: error: no matching function for call to 'f' f(0); ^ t.cpp:8:8: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~
Which could probably still be improved, although I can't convince myself that GCC 4.8 is doing us favors by producing the complete SFINAE backtrace.
I think that as a matter of principle, more information is better than less. The raw error message can relatively easily be parsed by a tool that filters out undesired detail, but if the detail is not emitted in the first place, it's gone. Regards, Nate

On 7/6/2012 5:43 PM, Nathan Ridge wrote:
From: doug.gregor@gmail.com On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <eric@boostpro.com> wrote:
On 7/6/2012 12:54 PM, Marc Glisse wrote:
On Fri, 6 Jul 2012, Eric Niebler wrote:
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. <snip informative backtrace>
Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll follow up with them.
FWIW, top-of-tree Clang produces:
t.cpp:12:3: error: no matching function for call to 'f' f(0); ^ t.cpp:8:8: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~
Which could probably still be improved, although I can't convince myself that GCC 4.8 is doing us favors by producing the complete SFINAE backtrace.
I think that as a matter of principle, more information is better than less. The raw error message can relatively easily be parsed by a tool that filters out undesired detail, but if the detail is not emitted in the first place, it's gone.
I'd like to second what Nate said. My experience is that not providing the backtrace is a usability nightmare. To me, this is *exactly* analogous to a template instantiation backtrace and should, IMO, be handled exactly the same way. If you want to snip the middle of the backtrace, fine, but show the start and the end (the deepest point, I guess), and perhaps provide an option for dumping the complete thing. My context is this: I'm rewriting proto in C++11. Entire expression tree transforms are function call chains like this. To simply be told, "nope, that transform can't be applied" but not why is maddening. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On Fri, Jul 6, 2012 at 6:17 PM, Eric Niebler <eric@boostpro.com> wrote:
On 7/6/2012 5:43 PM, Nathan Ridge wrote:
From: doug.gregor@gmail.com On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <eric@boostpro.com> wrote:
On 7/6/2012 12:54 PM, Marc Glisse wrote:
On Fri, 6 Jul 2012, Eric Niebler wrote:
The end result is that the user is presented with an error like: "no viable function A, template substitution failed." The user is given no information about which function in the chain failed to compile, or why. This is a serious usability problem!
... in your compiler.
All suggestions welcome,
Use g++. <snip informative backtrace>
Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll follow up with them.
FWIW, top-of-tree Clang produces:
t.cpp:12:3: error: no matching function for call to 'f' f(0); ^ t.cpp:8:8: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~
Which could probably still be improved, although I can't convince myself that GCC 4.8 is doing us favors by producing the complete SFINAE backtrace.
I think that as a matter of principle, more information is better than less. The raw error message can relatively easily be parsed by a tool that filters out undesired detail, but if the detail is not emitted in the first place, it's gone.
I'd like to second what Nate said. My experience is that not providing the backtrace is a usability nightmare. To me, this is *exactly* analogous to a template instantiation backtrace and should, IMO, be handled exactly the same way. If you want to snip the middle of the backtrace, fine, but show the start and the end (the deepest point, I guess), and perhaps provide an option for dumping the complete thing.
The typical complaints about template error messages concern excess verbosity rather than a lack of detail, so simply providing the full backtrace in all cases is not necessarily helpful. A more nuanced approach that provides this detail when it is interesting (e.g., only one candidate needs it) would be ideal; failing that, some command-line options to dial up the verbosity would allow experts to get the information they need while not flooding the terminals of the majority of C++ programmers. This is certainly worth of a Clang bug: http://llvm.org/bugs/ - Doug

On 7/9/2012 8:42 AM, Doug Gregor wrote:
On Fri, Jul 6, 2012 at 6:17 PM, Eric Niebler <eric@boostpro.com> wrote:
I'd like to second what Nate said. My experience is that not providing the backtrace is a usability nightmare. To me, this is *exactly* analogous to a template instantiation backtrace and should, IMO, be handled exactly the same way. If you want to snip the middle of the backtrace, fine, but show the start and the end (the deepest point, I guess), and perhaps provide an option for dumping the complete thing.
The typical complaints about template error messages concern excess verbosity rather than a lack of detail, so simply providing the full backtrace in all cases is not necessarily helpful.
OK, agreed.
A more nuanced approach that provides this detail when it is interesting (e.g., only one candidate needs it)
Not sure what you mean by "only one candidate needs it". I think there will be a challenge reporting these failures when there are overload sets in the call tree. You probably don't want to report the reason for failure for every overload at every level of the tree -- at least not by default.
would be ideal; failing that, some command-line options to dial up the verbosity would allow experts to get the information they need while not flooding the terminals of the majority of C++ programmers.
Yes, please. We don't want to have to tell people to try compiling with gcc if they don't understand clang's errors. :-P
This is certainly worth of a Clang bug: http://llvm.org/bugs/
http://llvm.org/bugs/show_bug.cgi?id=13309 -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 09/07/2012 21:32, Eric Niebler wrote:
Yes, please. We don't want to have to tell people to try compiling with gcc if they don't understand clang's errors. :-P
I've already been doing just that for a while. Clang error messages are not as good as they claim. The macro expansion, in particular, can easily lead to ridiculously verbose error messages.

On Fri, Jul 6, 2012 at 10:17 PM, Eric Niebler <eric@boostpro.com> wrote:
[snip]
I'd like to second what Nate said. My experience is that not providing the backtrace is a usability nightmare. To me, this is *exactly* analogous to a template instantiation backtrace and should, IMO, be handled exactly the same way. If you want to snip the middle of the backtrace, fine, but show the start and the end (the deepest point, I guess), and perhaps provide an option for dumping the complete thing.
+1 I have this problem all the time with skipped template instantiations in error messages in GCC. Sometimes I just can't know which code causes the problem because its instantiation isn't listed in the error message. This is *very annoying* and I just surrender and try with MSVC to find the root cause of the problem. It is not always the leaf, nor is always the first instantation as well. [snip]
-- Eric Niebler BoostPro Computing http://www.boostpro.com
Regards, -- Felipe Magno de Almeida

On 09/07/2012 17:58, Felipe Magno de Almeida wrote:
I have this problem all the time with skipped template instantiations in error messages in GCC. Sometimes I just can't know which code causes the problem because its instantiation isn't listed in the error message. This is *very annoying* and I just surrender and try with MSVC to find the root cause of the problem. It is not always the leaf, nor is always the first instantation as well.
The next GCC version (4.8) supports the -ftemplate-backtrace-limit=0 option to disable the limit. Clang supports it as well.

So, my question is: what can be done about this? Can we get the benefit of automatic type deduction in return types without causing SFINAE to drop functions from the overload set? Is there another trick we can use to report errors meaningfully when APIs are misused?
No! Just kidding. I was actually wondering something very similar earlier today. How can we intelligently document the causes of SFINAE-based overloading errors? I don't have a good answer yet. But I plugged in the problem you mentioned and checked it out with GCC 4.8 (compiled from trunk a month ago -- has it been so long?). template <typename T> auto h(T x) -> decltype(*x) { return *x; } template <typename T> auto g(T x) -> decltype(h(x)) { return h(x); } template <typename T> auto f(T x) -> decltype(g(x)) { return g(x); }; int main() { f(0); } And get this as output: foo.cpp: In function ‘int main()’: foo.cpp:22:6: error: no matching function for call to ‘f(int)’ f(0); ^ foo.cpp:22:6: note: candidate is: foo.cpp:18:8: note: template<class T> decltype (g(x)) f(T) auto f(T x) -> decltype(g(x)) { return g(x); }; ^ foo.cpp:18:8: note: template argument deduction/substitution failed: foo.cpp: In substitution of ‘template<class T> decltype (g(x)) f(T) [with T = int]’: foo.cpp:22:6: required from here foo.cpp:18:8: error: no matching function for call to ‘g(int&)’ foo.cpp:18:8: note: candidate is: foo.cpp:15:8: note: template<class T> decltype (h(x)) g(T) auto g(T x) -> decltype(h(x)) { return h(x); } ^ foo.cpp:15:8: note: template argument deduction/substitution failed: foo.cpp: In substitution of ‘template<class T> decltype (h(x)) g(T) [with T = int]’: foo.cpp:18:8: required by substitution of ‘template<class T> decltype (g(x)) f(T) [with T = int]’ foo.cpp:22:6: required from here foo.cpp:15:8: error: no matching function for call to ‘h(int&)’ foo.cpp:15:8: note: candidate is: foo.cpp:12:8: note: template<class T> decltype (* x) h(T) auto h(T x) -> decltype(*x) { return *x; } ^ foo.cpp:12:8: note: template argument deduction/substitution failed: foo.cpp: In substitution of ‘template<class T> decltype (* x) h(T) [with T = int]’: foo.cpp:15:8: required by substitution of ‘template<class T> decltype (h(x)) g(T) [with T = int]’ foo.cpp:18:8: required by substitution of ‘template<class T> decltype (g(x)) f(T) [with T = int]’ foo.cpp:22:6: required from here foo.cpp:12:27: error: invalid type argument of unary ‘*’ (have ‘int’) auto h(T x) -> decltype(*x) { return *x; } So GCC at least is documenting the root cause of the error. It would still be nice to customize error reporting for certain classes of failures in an API.

On 7/6/2012 1:15 PM, Andrew Sutton wrote:
So, my question is: what can be done about this? Can we get the benefit of automatic type deduction in return types without causing SFINAE to drop functions from the overload set? Is there another trick we can use to report errors meaningfully when APIs are misused?
No!
Just kidding. I was actually wondering something very similar earlier today. How can we intelligently document the causes of SFINAE-based overloading errors? I don't have a good answer yet.
I spent some time playing with this today. Attached is my best shot. The basic idea is to change the RETURNS macro to -- in addition to declaring the trailing return type and the function body -- also define an implicit conversion to a function pointer. That gets selected as the function called in the case that sfinae fails. That function returns a sfinae_error that contains some useful information: the text of the expression that failed to compile, the file and line number of the sfinae failure, and the types of the function parameters (mangled, unfortunately). It's used like this: struct S0 { template<typename T> auto operator()(T t) const RETURN( (t), t + 1 ) }; The first macro parameter is the function parameters. The second is the expression to evaluate. If all your function objects are declared this way, you can call them as normal. But if you get a sfinae failure anywhere in the call chain, the result of the call is a sfinae_error object with the goods. Can anybody do better? -- Eric Niebler BoostPro Computing http://www.boostpro.com

I spent some time playing with this today. Attached is my best shot. The basic idea is to change the RETURNS macro to -- in addition to declaring the trailing return type and the function body -- also define an implicit conversion to a function pointer. That gets selected as the function called in the case that sfinae fails. That function returns a sfinae_error that contains some useful information: the text of the expression that failed to compile, the file and line number of the sfinae failure, and the types of the function parameters (mangled, unfortunately).
Unfortunately, this results in a run-time error instead of compile-time.
Can anybody do better?
Attached is code that does present a compile-time error. It uses a function adaptor called `sfinae_error`. This will force sfinae success on the expression type dedcution by using a fail-through type. The fail through type doesn't matter since we know the function call will produce a compile error either way. So now, the `S0` class can be defined like this: struct S0_sfinae { template<typename T> auto operator()(T t) const RETURN( t + 1 ) }; typedef sfinae_error<S0_sfinae> S0; Then when you try to call it like this: struct foo {}; S2()(foo()); Clang will output the error inside of `sfinae_error` function adaptor with a full backtrace. At the bottom it will say: note: candidate template ignored: substitution failure [with T = foo]: invalid operands to binary expression ('foo' and 'int') auto operator()(T t) const RETURN( t + 1 ) Which is what you want. Futhermore, this can be used even when there are multiple overloads in the `S0` class.

On 8/11/2012 6:23 PM, paul Fultz wrote:
I spent some time playing with this today. Attached is my best shot. The basic idea is to change the RETURNS macro to -- in addition to declaring the trailing return type and the function body -- also define an implicit conversion to a function pointer. That gets selected as the function called in the case that sfinae fails. That function returns a sfinae_error that contains some useful information: the text of the expression that failed to compile, the file and line number of the sfinae failure, and the types of the function parameters (mangled, unfortunately).
Unfortunately, this results in a run-time error instead of compile-time.
Right. It can be easily turned into a compile-time error by static_assert'ing that the function's return type is not sfinae_error, but the compiler error wouldn't be informative. You'd have to run to code to see the error. I was hoping to have a way to transport the error to an API boundary and reporting it there, instead of presenting users with an imposing template instantiation backtrace, but that might not be possible.
Can anybody do better?
Attached is code that does present a compile-time error. It uses a function adaptor called `sfinae_error`. This will force sfinae success on the expression type dedcution by using a fail-through type. The fail through type doesn't matter since we know the function call will produce a compile error either way.
So now, the `S0` class can be defined like this:
struct S0_sfinae { template<typename T> auto operator()(T t) const RETURN( t + 1 ) };
typedef sfinae_error<S0_sfinae> S0;
Then when you try to call it like this:
struct foo {}; S2()(foo());
Clang will output the error inside of `sfinae_error` function adaptor with a full backtrace. At the bottom it will say:
note: candidate template ignored: substitution failure [with T = foo]: invalid operands to binary expression ('foo' and 'int') auto operator()(T t) const RETURN( t + 1 )
Which is what you want. Futhermore, this can be used even when there are multiple overloads in the `S0` class.
Great! And very simple. It doesn't move the error close to the API boundary, but other than that, it fits the bill. Thanks. If I end up using this technique in my code, I will credit you. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Right. It can be easily turned into a compile-time error by static_assert'ing that the function's return type is not sfinae_error, but the compiler error wouldn't be informative. You'd have to run to code to see the error.
I was hoping to have a way to transport the error to an API boundary and reporting it there, instead of presenting users with an imposing template instantiation backtrace, but that might not be possible.
I haven't followed this discussion, but you can move static_assert's up the call stack by moving the condition into an enable/disable_if, then the compiler simply doesn't find the function and you get the error "sooner". But you've probably considered all this already yours, John.

On 8/13/2012 12:46 AM, John Maddock wrote:
Right. It can be easily turned into a compile-time error by static_assert'ing that the function's return type is not sfinae_error, but the compiler error wouldn't be informative. You'd have to run to code to see the error.
I was hoping to have a way to transport the error to an API boundary and reporting it there, instead of presenting users with an imposing template instantiation backtrace, but that might not be possible.
I haven't followed this discussion, but you can move static_assert's up the call stack by moving the condition into an enable/disable_if, then the compiler simply doesn't find the function and you get the error "sooner".
But you've probably considered all this already yours, John.
John, what you're describing is precisely what we're trying to avoid: a mysterious "cannot find function" error because the function has been SFINAE'd out ... but where and why? The programmer needs better feedback than that. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 8/12/2012 7:50 PM, Eric Niebler wrote:
Great! And very simple. It doesn't move the error close to the API boundary, but other than that, it fits the bill. Thanks. If I end up using this technique in my code, I will credit you.
I think I have a very nice solution now. It combines my earlier approach with with Paul Fritz's. See the attached. You define your callables with the RETURNS macro and a try_call function object wrapper, like this: struct S0 { template<typename T> auto operator()(T t) const RETURN( t + 1 ) }; struct S1 { template<typename T> auto operator()(T t) const RETURN( try_call<S0>()(t) ) }; struct S2 { template<typename T> auto operator()(T t) const RETURN( try_call<S1>()(t) ) }; Then, you simply invoke your function object: auto i = S2()(32); // ok auto x = S2()(foo()); // compile-time error The compile error is transported to the API boundary. The result is a very short and precise error message about the cause of the failure. Clang gives this:
$ /usr/local/bin/clang++ -std=gnu++11 sfinae_error.cpp sfinae_error.cpp:28:26: error: no matching function for call to object of type 'S0' typedef decltype(std::declval<Fun>()(std::declval<Args>()...)) type; ^~~~~~~~~~~~~~~~~~~ sfinae_error.cpp:91:14: note: in instantiation of member function 'sfinae_error<S0 (foo &)>::what' requested here auto x = S2()(foo()); ^ sfinae_error.cpp:70:10: note: candidate template ignored: substitution failure [with T = foo]: invalid operands to binary expression ('foo' and 'int') auto operator()(T t) const RETURN( t + 1 ) ^ ~~~~~~~~~~~~~~~ 1 error generated.
Note that no information about the intermediate calls (S2, S1) shows up in the backtrace. Just the error you care about. GCC-4.7 gives a similarly terse error message. This, I think, is what I've been looking for. -- Eric Niebler BoostPro Computing http://www.boostpro.com
participants (9)
-
Andrew Sutton
-
Doug Gregor
-
Eric Niebler
-
Felipe Magno de Almeida
-
John Maddock
-
Marc Glisse
-
Mathias Gaunard
-
Nathan Ridge
-
paul Fultz