As I know, a possible implementation of std::result_of is
template<class F, class... ArgTypes>
struct result_of<F(ArgTypes...)>
{
typedef decltype(
std::declval<F>()(std::declval<ArgTypes>()...)
) type;
};
But when I use std::result_of I have some trouble.
int f(int x)
{
return 0;
}
template<typename T>
void Test(const T& t)
{
decltype(std::declval<T>()(std::declval<int>())) i1 = 1; // ok
typename std::result_of<T(int)>::type i2 = 2; // compile error:
// function returning a function
// I think this means the same thing just as above, right?
}
int main()
{
Test(f);
return 0;
}
What are the differences between these two forms?
std::result_of is declared in C++11 [meta.trans.other] Table 57 as:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
and it requires that:
Fn shall be a callable type (20.8.1), reference to function, or reference to callable type. The expression
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
shall be well formed.
callable type is defined in [func.def]/3:
A callable type is a function object type (20.8) or a pointer to member.
function object type is defined in [function.objects]/1:
A function object type is an object type (3.9) that can be the type of the postfix-expression in a function call (5.2.2, 13.3.1.1). ...
In your program, f is a reference to a function of type int(int), so T is deduced to the function type int(int). Note that a function type is not one of the valid options for the type Fn to be passed to std::result_type. A reference to function is an acceptable type, however: you should pass the full type of Tests parameter to result_of instead of only T (Demo at Coliru):
template<typename T>
void Test(const T&)
{
decltype(std::declval<T>()(std::declval<int>())) i1 = 1;
typename std::result_of<const T& (int)>::type i2 = 2;
}
Regarding the difference between the two forms, remember that std::declval always returns a reference type; specifically std::declval<T>() returns T&&. So
decltype(std::declval<T>()(std::declval<int>()))
is asking for the what type is returned when a T&& is invoked with an int&& argument.
[basic.compound] describes what function types look like in C++:
— functions, which have parameters of given types and return void or references or objects of a given type
Therefore, the return type part of a function type must not itself be a function type, and thus T(int) is not a valid type in the C++ type system when T = int(int).
Moreover, [dcl.fct]/8 clarifies:
Functions shall not have a return type of type array or function
Note also that the actual analogue of your i1 line is typename std::result_of<T>::type i2.
Related
Universal references (i.e. "forward references", the c++ standard name) and perfect forwarding in c++11, c++14, and beyond have many important advantages; see here, and here.
In Scott Meyers' article referenced above (link), it is stated as a rule of thumb that:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
Example 1
Indeed, using clang++ we see that the following code snippet will successfully compile with -std=c++14:
#include <utility>
template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}
int x1 = 1;
int const x2 = 1;
int& x3 = x1;
int const& x4 = x2;
// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1); // various lvalues okay, as expected
auto r2 = f (x2); // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected
Given any description of universal references (forward references) and type deduction (see, for instance, this explanation) it is clear why the above works. Although, from the same explanation, it is not abundantly clear why the below fails to work as well.
(failed) Example 2
This question addresses the same issue. The provided answers do not, however, explain why templated types are not categorized as being "deduced".
What I am about to show (seemingly) satisfies the requirement stated above by Meyers. However, the following code snipped fails to compile, producing the error (among others for each call to f):
test.cpp:23:11: error: no matching function for call to 'f'
auto r1 = f (x1);
test.cpp:5:16: note: candidate function [with T = foo, A = int] not
viable: no known conversion from 'struct foo< int >' to 'foo< int > &&'
for 1st argument
decltype(auto) f (T< A > && t)
#include <utility>
//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works
In context, since the type T<A> of f's parameter is deduced, surely the parameter declaration T<A>&& t would behave as a universal reference (forward reference).
Example 3 (for clarity in describing the problem at hand)
Let me stress the following: the failure of the code in Example 2 to compile is not due to the fact that struct foo<> is a templated type. The failure seems to be cause only by the declaration of f's parameter as a templated type.
Consider the following revision to the previous code, which now does compile:
#include <utility>
//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}
//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
It is astonishing to me that this simple change completely alters the behaviour of the type deduction for the template function f's type parameter.
Questions:
Why does the second example not work as expected? Are there techniques to overcome this problem with templated types in c++11/14? Are there well known, extant codebases (in the wild) making successful use of c++'s forward references with templated types?
When you call some function f with some lvalue:
int a = 42;
f(a);
Then f must be able to accept such an lvalue. This is the case when the first parameter of f is a (lvalue) reference type, or when it's not a reference at all:
auto f(int &);
auto f(int); // assuming a working copy constructor
This won't work when the parameter is a rvalue reference:
auto f(int &&); // error
Now, when you define a function with a forwarding reference as first parameter as you did in the first and third example ...
template<typename T>
auto f(T&&); // Showing only declaration
... and you actually call this function with an lvalue, template type deduction turns T into an (lvalue) reference (that this happens can be seen in the example code I provide in a moment):
auto f(int & &&); // Think of it like that
Surely, there are too much references involved above. So C++ has collapsing rules, which are actually quite simple:
T& & becomes T&
T& && becomes T&
T&& & becomes T&
T&& && becomes T&&
Thanks to the second rule, the "effective" type of the first parameter of f is a lvalue reference, so you can bind your lvalue to it.
Now when you define a function g like ...
template<template<class> class T, typename A>
auto g(T<A>&&);
Then no matter what, template parameter deduction must turn the T into a template, not a type. After all, you specified exactly that when declaring the template parameter as template<class> class instead of typename.
(This is an important difference, foo in your example is not a type, it's a template ... which you can see as type level function, but back to the topic)
Now, T is some kind of template. You cannot have a reference to a template.
A reference (type) is built from a (possibly incomplete) type. So no matter what, T<A> (which is a type, but not a template parameter which could be deduced) won't turn into an (lvalue) reference, which means T<A> && doesn't need any collapsing and stays what it is: An rvalue reference. And of course, you cannot bind an lvalue to an rvalue reference.
But if you pass it an rvalue, then even g will work.
All of the above can be seen in the following example:
template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};
f(thing<int> {}); // "not a reference"
f(it); // "a reference"
// T = thing<int> &
// T&& = thing<int>& && = thing<int>&
g(thing<int> {}); // works
//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&
return 0;
}
(Live here)
Concerning how one could "overcome" this: You cannot. At least not the way you seem to want it to, because the natural solution is the third example you provide: Since you don't know the type passed (is it an lvalue reference, a rvalue reference or a reference at all?) you must keep it as generic as T. You could of course provide overloads, but that would somehow defeat the purpose of having perfect forwarding, I guess.
Hm, turns out you actually can overcome this, using some traits class:
template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};
You can then extract both the template and the type the template was instantiated with inside of the function:
template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}
(Live here)
[...] can you explain why it is not an universal reference? what would the danger or the pitfall of it be, or is it too difficult to implement? I am sincerely interested.
T<A>&& isn't an universal reference because T<A> isn't a template parameter. It's (after deduction of both T and A) a simple (fixed / non generic) type.
A serious pitfall of making this a forwarding reference would be that you could no longer express the current meaning of T<A>&&: An rvalue reference to some type built from the template T with parameter A.
Why does the second example not work as expected?
You have two signatures:
template <typename T>
decltype(auto) f (T&& );
template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );
f takes a forwarding reference, but f2 does not. The specific rule, from [temp.deduct.call] is, bold emphasis mine:
A forwarding reference is an rvalue
reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
With f, the argument is an rvalue reference to a template parameter (T). But with f2, T<A> is not a template parameter. Thus, that function simply takes as its argument an rvalue reference to T<A>. The calls fail to compile because all your arguments are lvalues and this case has no special exception for being called with an lvalue.
As for overcoming the problem, I guess a more or less equivalent way would be to deduce it with a forward reference, and trigger the comparison with T<A> manually.
template<typename T>
class X;
template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;
template<typename _>
using template_ = T<_>;
};
template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};
template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}
If you don't actually want T<A> but a specific type, the best way is to use std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>, which is way easier, I guess.
int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}
It is not enough to have type deduction. The form of the type declaration must be exactly T&& (an rvalue reference to just a template parameter). If it's not (or there is no type deduction) the parameter is an rvalue reference. If the argument is an lvalue, it won't compile. Since T<A>&& does not have that form, f (T<A> && t) is unable to accept an lvalue (as an lvalue reference) and you get the error. If you think that requires too much generality, consider that a simple const qualifier breaks it too:
template<typename T>
void f(const T&& param); // rvalue reference because of const
(putting aside the relative uselessness of a const rvalue reference)
The rules for reference collapsing simply do not kick in unless the most general form T&& is used. Without the ability for f to recognize an lvalue argument was passed and treat the parameter as an lvalue reference, there is no reference collapsing to be done (i.e. collapsing T& && to T& can't happen and it's just T<something>&&, an rvalue ref. to a templated type). The needed mechanism for the function to determine whether an rvalue or an lvalue is passed as an argument is encoded in the deduced template parameter. However, this encoding only occurs for a universal reference parameter, as strictly defined.
Why is this level of generality is necessary (besides just being the rule)? Without this specific definition format, universal references could not be super-greedy functions that instantiate to capture any type of argument... as they are designed to be. Daniel's answer gets to the point, I think: Suppose you want to define a function with a regular rvalue reference to a templated type parameter, T<A>&& (i.e. that does not accept an lvalue argument). If the following syntax were treated as a universal reference, then how would you change it to specify a regular rvalue reference?
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?
There needs to be a way to explicitly define the parameter as an rvalue reference to exclude lvalue arguments. This reasoning would seem to apply for other types of parameters, including cv qualifications.
Also, there seem to be ways around this (see traits and SFINAE), but I can't answer that part. :)
Why the compiler is not able to deduce the template parameter for std::forward?
I mean:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctor\n"; }
A( X&& ) { std::cout << "move ctor\n"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
I know this is a design choice (due to the std::remove_reference in the definition of std::forward) to avoid the user forget to specify the type. What I can't get is: why the way it's implemented works to prevent deduction? Why the compiler is not just deducing forward's template parameter as Arg.
std::forward is declared like so:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type is a non-deduced context. The compiler has no way of knowing which T should be deduced because it doesn't understand the semantic connection between the type member type and a given T. It would need to search through all types to find a match and be able to somehow disambiguate collisions. This is unreasonable, so the standard doesn't allow it.
The reason you have to specify a type for forward, by design, is what happens to a inside the function:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
Since a is always an lvalue, there isn't enough information in a itself to be able to determine if it was passed in as an lvalue or rvalue. That information is only available via the type Arg, which will be either X or X&. Without that extra type information, it's impossible to know whether or now a must be forwarded as an lvalue or rvalue... which is why you need to provide it:
return new T(std::forward<Arg>(a));
}
From C++11 standard:
14.8.2.5 Deducing template arguments from a type
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id
— The expression of a decltype-specifier.
— A non-type template argument or an array bound in which a
subexpression references a template parameter.
— A template parameter used in the parameter type of a function
parameter that has a default argument that is being used in the call
for which argument deduction is being done.
etc...
std::forward is declared like this:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
According to first sentence above:
typename std::remove_reference<_Tp>::type is non deduced context.
I've encountered a pretty weird behavior when using auto and dynamic_cast.
This is the class hierachy i have:
class BaseInterface {
public:
virtual void someMethod()=0;
};
class Derived:public BaseInterface {
public:
virtual void someMethod1()=0;
void someMethod()override;
};
And of course there are some classes that implement all derived methods.
Then there is a third class which looks like this:
class ThirdClass {
public:
void demoMethod(BaseInterface&);
void anotherMethod(Derived&);
};
void ThirdClass::demoMethod(BaseInterface& obj) {
auto buffer=dynamic_cast<Derived&>(obj);
anotherMethod(buffer);
}
When i compile this with gcc i get an "cannot allocate an object of abstract type" error. Whereas when i replace
auto buffer=...
with
Derived& buffer=...
everything compiles fine. Why is that the case? Is auto not deducing the right type or something?
Also i found a dirty trick to still use auto:
void ThirdClass::demoMethod(Base& obj) {
auto buffer=dynamic_cast<Derived*>(&obj);
anotherMethod(*buffer);
}
You're getting Derived from auto. Use this instead:
auto & buffer = dynamic_cast<Derived&>(obj);
§7.1.6.4/7:
When a variable declared using a placeholder type is initialized […]
the deduced return type or variable type is determined from the type
of its initializer. […] let T be the declared type of the variable
or return type of the function. If the placeholder is the auto
type-specifier, the deduced type is determined using the rules for
template argument deduction. […] obtain P from T by replacing the
occurrences of auto with either a new invented type template
parameter U[…]. Deduce a value for U using the rules of template
argument deduction from a function call (14.8.2.1), where P is a
function template parameter type and the corresponding argument is the
initializer.
So, in order to familiarize yourself with the process, take a look at the actual rule used for deducing the type of buffer: What happens if you change
template <typename U>
void f( U );
to
void f( Derived& );
when calling f with an lvalue of type Derived? Clearly, for the function template, U will be deduced as Derived, which then yields a deduction failure.
This directly corresponds to the deduction of the placeholder type in your example - auto will be replaced by Derived, and that fails, as Derived is abstract.
Generally speaking, if you write
auto obj = …;
obj will never be a reference, just as U will never be deduced as a reference type when calling the above function template.
Instead, use auto&:
auto& buffer = dynamic_cast<Derived&>(obj);
Now, P is U&:
template <typename U>
void f(U&);
U is, of course, still deduced as Derived, but the type of P - which is effectively the type of buffer - is Derived&.
I have the following code.
template <typename... Types>
void print_tuple(const std::tuple<Types&&...>& value)
{
std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl;
}
print_tuple(std::forward_as_tuple("test",1));
which compiler complains about
error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’
print_tuple(std::forward_as_tuple("test",1));
why does compiler deduce the type of the first element in the tuple to be const char (&&)[5]?
Generally speaking, for deduction to succeed, the argument needs to have the same general form as the parameter. There are some exceptions where T && can be deduced from U & (by selecting T = U &), but no such exception was specified for this case.
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]
8 A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
[...]
T&
T&&
[...]
It's not exactly clear, but this requires P (the parameter) and A (the argument) to both have the same form. They need to both be of the T& form, or both of the T&& form. The exceptions, the circumstances where T && can be deduced from U &, are done by changing T && to plain T before the matching takes place, in limited circumstances:
10 Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2) or when deducing template arguments from a function declaration (14.8.2.6) and Pi and
Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is an rvalue reference to a cv-unqualified template parameter and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [...]
and
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]
3 [...] If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type "lvalue reference to A" is used in place of A for type deduction. [...]
but no similar exception applies to your scenario.
It's this same principle that renders
template <typename T> struct S { };
template <typename T> void f(S<const T>) { }
int main() { f(S<void()>()); }
invalid: const T cannot be deduced from void(), even though T = void() would give exactly that result, and calling f<void()> would succeed.
Wintermute's deleted answer showed that you can use
template <typename... Types> // vv-- change here
void print_tuple(const std::tuple<Types...>& value)
instead: this allows Types to be deduced as lvalue references, as rvalue references, or as non-references, depending on the type of value.
Did you intend to use && in std::tuple<Types&&...> as a universal reference? This is not universal reference; it is rvalue reference and can only bind to rvalues. You can do like this to check what kind of reference it is:
template<typename T>
class TD;
Then define your template function:
template <typename... Types>
void print_tuple(const std::tuple<Types&&...>& value)
{
TD<decltype(value)> a;
std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl;
}
Then you will see the compilation error like:
implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>'
You can see that even for the int type, it deduces to be rvalue reference. Rvalue references cannot bind to lvalues. You can try that by calling:
int i = 1;
print_tuple(std::forward_as_tuple(i,1)); // error.
Therefore, it is correct that const char(&&)[5] is deduced, and a string literal cannot be converted to const char(&&)[5]. If you call print_tuple like:
print_tuple(std::forward_as_tuple(string("test"),1));
It will work. Now the type is tuple<string&&, int&&>.
I am trying to pass a generic lambda function to a boost::fusion::fold function so that I can iterate all the elements of a boost::fusion::vector. My goal is to call a non-const member function from each element in the vector. The problem is that even though the vector holds non-const values the type deduced by the generic lambda is a const reference. This results in my gcc-4.9.0 compiler (using CygWin) complaining that I am discarding the const qualifier.
#include <iostream>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/fold.hpp>
#include <boost/fusion/include/for_each.hpp>
class Silly {
public:
Silly(int x)
: x_(x){}
int increment(int i) {
return x_ += i;
}
private:
int x_;
};
using my_type = boost::fusion::vector<Silly, Silly>;
int main() {
my_type my_vector(1, 2);
boost::fusion::fold(my_vector, 0, [](int i, auto& x){return x.increment(i);}); //error: passing 'const Silly' as 'this' argument of 'int Silly::increment(int)' discards qualifiers
}
Now, if instead of the lambda I pass the following functor, the program compiles cleanly
struct functor {
template <class X>
int operator()(int i, X& x) {
return x.increment(i);
}
};
Is this a boost::fusion bug or am I missing something? Thanks in advance!
There are multiple boost::fusion::fold overloads. From boost's svn repo:
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq const
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State const& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State const,F>::call(
state,
seq,
f);
}
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq& seq, State& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq,State,F>::call(
state,
seq,
f);
}
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq const
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State,F>::call(
state,
seq,
f);
}
The compiler is allowed to instantiate the class template result_of::BOOST_FUSION_FOLD_NAME (*) in the return type for all these variants once type deduction and substitution have succeeded, before an overload is selected. In this case, the compiler must instantiate this class template in order to determine whether or not the return type is valid. If substitution (of the template arguments) in the return type leads to an invalid type in the immediate context, the overload is discarded. This is known as SFINAE.
(*) This name typically resolves to result_of::fold.
The instantiation of one of the overloads that has a Seq const& parameter tries now to determine the return type of the lambda. However, instantiating the lambda with a Silly const& second argument fails: increment cannot be called on a const object (this is what the compiler tells you).
If determining the return type fails, this should lead to a substitution failure in the fold overload we're trying to determine the return type of. However, substitution failures due to automatic return type deduction in lambdas and C++14 functions are not in the immediate context of the original template fold: They happen within the function that uses automatic return type deduction (here: the lambda).
A substitution failure not in the immediate context of the original template is a hard error, it is not a SFINAE-type error that you could recover from. (SFINAE = SFIICINAE)
If you explicitly specify the return type of the lambda, [](int i, auto& x) -> int {return x.increment(i);}, the function/lambda does not need to be instantiated to determine the return type. It can be determined from the declaration alone. Therefore, no substitution failure based on the return type happens for any of the overloads, and usual overload resolution can select an appropriate overload. The non-const Seq& overload is chosen, and the instantiation of the lambda will be valid.
Similarly, for the explicitly written functor: If the return type can be determined without instantiating the function, no error will occur. If you use C++14's return type deduction for ordinary functions, the same problem occurs:
struct functor {
template <class X>
auto operator()(int i, X& x) {
return x.increment(i);
}
};
As a side remark: As T.C. noted in a comment, a hard error also occurs for the following function object type:
struct functor {
int operator()(int i, Silly& x) {
return x.increment(i);
}
};
The reason this fails is different, though:
Again, all fold overloads need to instantiate the result_of::fold class template with their respective types. This class template however does not produce substitution errors in the immediate context: If the function passed cannot be called with the argument types passed, a hard error will occur.
Since a function of the type int(int, Silly&) cannot be called with arguments of the type int and Silly const&, a hard error occurs.
When writing the apply operator as a template (as in the example with C++14 return type deduction), the declaration of the operator() template can be instantiated for an second argument of type Silly const& (X will be deduced to be Silly const). The function definition cannot be instantiated, though, as this will result in the same error as in the OP: Silly::increment requires a non-const Silly object.
The instantiation of the definition of the function however happens only after overload resolution if there is no return type deduction. Therefore, this will not produce substitution failures.