Variadic Templates pack expansions - c++

In Andrei's talk on GoingNative 2012 he talks about Variadic Templates, and he explains at one point by way of the example underneath how the parameter pack expansions work. Being fairly new to this subject I found it fairly hard to follow how each case works, could anybody please explain how the expansion works in each function call of gun?
template<class... Ts> void fun(Ts... vs) {
gun(A<Ts...>::hun(vs)...);
gun(A<Ts...>::hun(vs...));
gun(A<Ts>::hun(vs)...);
}

1.
gun(A<Ts...>::hun(vs)...)
=> gun(A<T1, T2, …, Tn>::hun(vs)...)
=> gun(A<T1, T2, …, Tn>::hun(v1),
A<T1, T2, …, Tn>::hun(v2),
…,
A<T1, T2, …, Tn>::hun(vm))
2.
gun(A<Ts...>::hun(vs...))
=> gun(A<T1, T2, …, Tn>::hun(vs...))
=> gun(A<T1, T2, …, Tn>::hun(v1, v2, …, vm))
This should be obvious.
3.
gun(A<Ts>::hun(vs)...)
=> gun(A<T1>::hun(v1), A<T2>::hun(v2), …, A<Tn>::hun(vn))
(In this case the program won't compile if the lengths of Ts and vs differ)
The ... will expand a pattern (which includes any parameter packs) preceding it, meaning that, in foo(Ts, Us, Vs)..., each member of the list Ts, Us, Vs (enumerated in lock step) will be substituted into that pattern, and a comma separated list will be formed:
foo(Ts, Us, Vs)...
=> foo(T1, U1, V1), foo(T2, U2, V2), …, foo(Tn, Un, Vn)
And if there are nested expansions, the innermost patterns will be expanded first. Therefore, in case 1, the pattern Ts will first be expanded into T1, T2, …, Tn. And then, the pattern preceding the outer ... is A<T1, T2, …, Tn>::fun(vs) — note that Ts has been expanded — so it will be expanded to A<T1, T2, …, Tn>::fun(v1), A<T1, T2, …, Tn>::fun(v2), …, A<T1, T2, …, Tn>::fun(vm) by substituting v1, v2, etc. into vs.

KennyTM's answer is perfect. I just also like samples. But since his answer is abstract, I didn't feel like adding demos to his answer is the correct thing. So demos for his answer are here. I'm assuming his answer is right, I know nothing myself. (If you upvote this, upvote his too)
Obviously this is all psudocode just showing the expanded states.
void foo<void*,int,char,std::string>(nullptr, 32, '7', "BANANA") {
//gun(A<Ts...>::hun(vs)...);
gun(A<void*,int,char,std::string>::hun(nullptr)
,A<void*,int,char,std::string>::hun(32)
,A<void*,int,char,std::string>::hun('7')
,A<void*,int,char,std::string>::hun("BANANA")
);
//gun(A<Ts...>::hun(vs...));
gun(A<void*,int,char,std::string>::hun(nullptr, 32, '7', "BANANA");
//gun(A<Ts>::hun(vs)...);
gun(A<void*>::hun(nullptr)
,A<int>::hun(32),
,A<char>::hun('7'),
,A<std::string>::hun("BANANA")
);
}

Related

Clang cannot deduce empty variadic template arguments not put at end of function arguments [duplicate]

I am wondering why the following code doesn't compile:
struct S
{
template <typename... T>
S(T..., int);
};
S c{0, 0};
This code fails to compile with both clang and GCC 4.8. Here is the error with clang:
test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
S(T..., int);
^
It seems to me that this should work, and T should be deduced to be a pack of length 1.
If the standards forbids doing things like this, does anyone know why?
Because when a function parameter pack is not the last parameter, then the template parameter pack cannot be deduced from it and it will be ignored by template argument deduction.
So the two arguments 0, 0 are compared against , int, yielding a mismatch.
Deduction rules like this need to cover many special cases (like what happens when two parameter packs appear next to each other). Since parameter packs are a new feature in C++11, the authors of the respective proposal drafted the rules conservatively.
Note that a trailing template parameter pack will be empty if it is not otherwise deduced. So when you call the constructor with one argument, things will work (notice the difference of template parameter pack and function parameter pack here. The former is trailing, the latter is not).
So, there should be a workaround. Something along these lines:
namespace v1 {
// Extract the last type in a parameter pack.
// 0, the empty pack has no last type (only called if 1 and 2+ don't match)
template<typename... Ts>
struct last_type {};
// 2+ in pack, recurse:
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};
// Length 1, last type is only type:
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
}
namespace v2 {
template<class T> struct tag_t{using type=T;};
template<class T> using type_t = typename T::type;
template<class...Ts>
using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
template<class...Ts>
struct last_type {
using type=last<Ts...>;
};
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1
struct S
{
// We accept any number of arguments
// So long as the type of the last argument is an int
// probably needs some std::decay to work right (ie, to implicitly work out that
// the last argument is an int, and not a const int& or whatever)
template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
S(T...);
};
where we check that the last type of a parameter pack is an int, or that we where only passed an int.
I am actually a little interested in the same thing (wanting to specialize templated parameter packs based on the final arguments).
I believe there may be a path forward by combining tuple reversal (std::make_tuple, back-port std::apply for C++14, etc):
How to reverse the order of arguments of a variadic template function?
Will get back on here if it is successful.
Related posts:
Parameters after parameter pack in function
Parameter with non-deduced type after parameter pack
EDIT:
Yup, figured it out after a bit; not perfect, as there are extra copies flying around, but it's a start.
If you know a simpler way than what I list below, please don't hesitate to post!
TL;DR
Can do stuff like this:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
And then implement this pseduo code:
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
By doing something like:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
Using the above utilities.
Has some (a lot of) drawbacks. Will list them below.
Scope
This is for users of C++14 (maybe C++11), who want to borrow from the future (C++17).
Step 1: Reverse arguments
There are a few different ways to do this. I've listed out some alternatives in this example:
tuple.cc - Playground for two alternatives (credits in the source code):
Use foldable expressions and manipulate the index passed via std::apply_impl (credit: Orient).
Use recursive templates to construct a reversed index_sequence (credit: Xeo)
tuple.output.txt - Example output
This prints out the reversed_index_sequence template from Xeo's example. I needed this for debugging.
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
I chose Alternative 1, as it's easier for me to digest.
I then tried to formalize it right quick:
tuple_future.h - Borrowing from the future (namespace stdfuture), and making an extension (namespace stdcustom)
tuple_future_main.cc - Simple, advanced, and useful (see below) examples using the above
tuple_future_main.output.txt - Example output
Definition Snippets (adaptation of C++17 possible implementation of std::apply on cppreference.com):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// #ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
Usage Snippets: (from tuple_future_main.output.txt, copied from above)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
Step 2: Buckle your shoe (with reversed parameter packs)
First, establish the patterns for the final arguments that you wish to use. You will have to explicitly enumerate these, as you can only have one parameter pack.
(Taken from tuple_future_main.cc):
Example Scenario:
We like to add things to containers with a name, something of the form:
add_item(const Item& item, const string& name, Container& c)
We can also construct an Item with a [awfully large] number of overloads, and
we have convenenience overloads:
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
To do so, we can declare the following:
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
And then define our generic interfaces:
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
Now we can do stuff like: (taken from tuple_future_main.output.txt)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
Note the extra copy constructors... :(
Drawbacks
Ugly as hell
May not be useful
It could be easier to just refactor your interfaces
However, this could be used as a stop-gap to transition to a more generalized interface.
Possibly fewer lines to delete.
Especially if it plugs your development process with template explosions
Can't nail down where the extra copies are coming from.
It may be due to judicious usage of variadic lambdas
You have to carefully craft your base functionality
You shouldn't try to extend an existing function.
Parameter packs will be greedy in how they match to functions
You either need to explicitly spell out each overload you want, or bow down and let the variadic parameter pack dispatch to your desired functionality
If you find an elegant way around this, please let me know.
Template errors are shitty.
Granted, not too shitty. But it's hard to infer that you missed an available overload.
Wraps a lot of simple functionality in lambdas
You may be able to use make_reversed_index_sequence and directly dispatch to the function (mentioned in other SO posts). But that's painful to repeat.
Todo
Get rid of extra copies
Minimize the need for all the lambdas
Not necessary if you have a Callable
Try to combat parameter pack greediness
Is there a generalized std::enable_if matching that matches to both lvalue- and rvalue-references, and possibly handle forwarding compatible implicit copy constructors?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically
Hopes
Maybe C++17 will support non-final parameter pack arguments, such that all of this can be discarded... fingers crossed
From the working draft of the standard N3376 § 14.1 is a probable section to read about this.
Below is § 14.1.11
If a template-parameter of a class template or alias template has a
default template-argument, each subsequent template-parameter shall
either have a default template-argument supplied or be a template
parameter pack. If a template-parameter of a primary class template or
alias template is a template parameter pack, it shall be the last
template-parameter. A template parameter pack of a function template
shall not be followed by another template parameter unless that
template parameter can be deduced from the parameter-type-list of the
function template or has a default argument.

C++11 variadic function call with std::function [duplicate]

I am wondering why the following code doesn't compile:
struct S
{
template <typename... T>
S(T..., int);
};
S c{0, 0};
This code fails to compile with both clang and GCC 4.8. Here is the error with clang:
test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
S(T..., int);
^
It seems to me that this should work, and T should be deduced to be a pack of length 1.
If the standards forbids doing things like this, does anyone know why?
Because when a function parameter pack is not the last parameter, then the template parameter pack cannot be deduced from it and it will be ignored by template argument deduction.
So the two arguments 0, 0 are compared against , int, yielding a mismatch.
Deduction rules like this need to cover many special cases (like what happens when two parameter packs appear next to each other). Since parameter packs are a new feature in C++11, the authors of the respective proposal drafted the rules conservatively.
Note that a trailing template parameter pack will be empty if it is not otherwise deduced. So when you call the constructor with one argument, things will work (notice the difference of template parameter pack and function parameter pack here. The former is trailing, the latter is not).
So, there should be a workaround. Something along these lines:
namespace v1 {
// Extract the last type in a parameter pack.
// 0, the empty pack has no last type (only called if 1 and 2+ don't match)
template<typename... Ts>
struct last_type {};
// 2+ in pack, recurse:
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};
// Length 1, last type is only type:
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
}
namespace v2 {
template<class T> struct tag_t{using type=T;};
template<class T> using type_t = typename T::type;
template<class...Ts>
using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
template<class...Ts>
struct last_type {
using type=last<Ts...>;
};
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1
struct S
{
// We accept any number of arguments
// So long as the type of the last argument is an int
// probably needs some std::decay to work right (ie, to implicitly work out that
// the last argument is an int, and not a const int& or whatever)
template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
S(T...);
};
where we check that the last type of a parameter pack is an int, or that we where only passed an int.
I am actually a little interested in the same thing (wanting to specialize templated parameter packs based on the final arguments).
I believe there may be a path forward by combining tuple reversal (std::make_tuple, back-port std::apply for C++14, etc):
How to reverse the order of arguments of a variadic template function?
Will get back on here if it is successful.
Related posts:
Parameters after parameter pack in function
Parameter with non-deduced type after parameter pack
EDIT:
Yup, figured it out after a bit; not perfect, as there are extra copies flying around, but it's a start.
If you know a simpler way than what I list below, please don't hesitate to post!
TL;DR
Can do stuff like this:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
And then implement this pseduo code:
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
By doing something like:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
Using the above utilities.
Has some (a lot of) drawbacks. Will list them below.
Scope
This is for users of C++14 (maybe C++11), who want to borrow from the future (C++17).
Step 1: Reverse arguments
There are a few different ways to do this. I've listed out some alternatives in this example:
tuple.cc - Playground for two alternatives (credits in the source code):
Use foldable expressions and manipulate the index passed via std::apply_impl (credit: Orient).
Use recursive templates to construct a reversed index_sequence (credit: Xeo)
tuple.output.txt - Example output
This prints out the reversed_index_sequence template from Xeo's example. I needed this for debugging.
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
I chose Alternative 1, as it's easier for me to digest.
I then tried to formalize it right quick:
tuple_future.h - Borrowing from the future (namespace stdfuture), and making an extension (namespace stdcustom)
tuple_future_main.cc - Simple, advanced, and useful (see below) examples using the above
tuple_future_main.output.txt - Example output
Definition Snippets (adaptation of C++17 possible implementation of std::apply on cppreference.com):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// #ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
Usage Snippets: (from tuple_future_main.output.txt, copied from above)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
Step 2: Buckle your shoe (with reversed parameter packs)
First, establish the patterns for the final arguments that you wish to use. You will have to explicitly enumerate these, as you can only have one parameter pack.
(Taken from tuple_future_main.cc):
Example Scenario:
We like to add things to containers with a name, something of the form:
add_item(const Item& item, const string& name, Container& c)
We can also construct an Item with a [awfully large] number of overloads, and
we have convenenience overloads:
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
To do so, we can declare the following:
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
And then define our generic interfaces:
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
Now we can do stuff like: (taken from tuple_future_main.output.txt)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
Note the extra copy constructors... :(
Drawbacks
Ugly as hell
May not be useful
It could be easier to just refactor your interfaces
However, this could be used as a stop-gap to transition to a more generalized interface.
Possibly fewer lines to delete.
Especially if it plugs your development process with template explosions
Can't nail down where the extra copies are coming from.
It may be due to judicious usage of variadic lambdas
You have to carefully craft your base functionality
You shouldn't try to extend an existing function.
Parameter packs will be greedy in how they match to functions
You either need to explicitly spell out each overload you want, or bow down and let the variadic parameter pack dispatch to your desired functionality
If you find an elegant way around this, please let me know.
Template errors are shitty.
Granted, not too shitty. But it's hard to infer that you missed an available overload.
Wraps a lot of simple functionality in lambdas
You may be able to use make_reversed_index_sequence and directly dispatch to the function (mentioned in other SO posts). But that's painful to repeat.
Todo
Get rid of extra copies
Minimize the need for all the lambdas
Not necessary if you have a Callable
Try to combat parameter pack greediness
Is there a generalized std::enable_if matching that matches to both lvalue- and rvalue-references, and possibly handle forwarding compatible implicit copy constructors?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically
Hopes
Maybe C++17 will support non-final parameter pack arguments, such that all of this can be discarded... fingers crossed
From the working draft of the standard N3376 § 14.1 is a probable section to read about this.
Below is § 14.1.11
If a template-parameter of a class template or alias template has a
default template-argument, each subsequent template-parameter shall
either have a default template-argument supplied or be a template
parameter pack. If a template-parameter of a primary class template or
alias template is a template parameter pack, it shall be the last
template-parameter. A template parameter pack of a function template
shall not be followed by another template parameter unless that
template parameter can be deduced from the parameter-type-list of the
function template or has a default argument.

Why is std::make_tuple(7 + N...) legal in C++11?

The following code is legal in C++11.
template<int... N>
std::tuple<decltype(N)...> f()
{
return std::make_tuple(7 + N...);
}
What does it mean?
First of all, look at the template parameters: template <int ... N>. Even though a variable number of template arguments can be given to f, all of them must be of type int.
Now when you use f<t1, t2, ..., tn>, the parameter unpacking (7 + N...) will follow the pattern 7 + N and expand to
7 + t1, 7 + t2, 7 + t3, ..., 7 + tn
Therefore you end up with a tuple which contains each of your template arguments increased by seven. The details can be found in section 14.5.3 Variadic templates [temp.variadic].
3. A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list [...].

What's the difference between doing vector<vector<T...>> and vector<vector<T>...>

I saw code like this earlier:
using A = std::vector<std::vector<T>...>
where T is a variadic list of template arguments. I wanted to know what the difference is between putting the parameter pack at the end of the last angle bracket and the first. For example:
using B = std::vector<std::vector<T...>>;
Both of these two compile fine but I don't know what the difference is.
Can someone explain? Thanks.
In a pack expansion the pattern that precedes the ... is repeated for each element of the pack, so vector<T>... means expand into vector<T1>, vector<T2>, vector<T3> whereas vector<T...> means expand into vector<T1, T2, T3>
If the parameter pack only has one element they're the same, but consider if the parameter pack has two elements, it should be obvious that
std::vector<std::vector<T1>, std::vector<T2>>
and
std::vector<std::vector<T1, T2>>
are not the same. The first one will not compile, the second template parameter for std::vector must be an allocator type, not a vector. The second one will compile if T2 is an allocator type.
When instantiating A<T1, T2, T3>, it is expanded to:
std::vector<std::vector<T1>, std::vector<T2>, std::vector<T3>>
Using the same template arguments to instantiate B, you get:
std::vector<std::vector<T1, T2, T3>>

Can tr1::function swallow return values?

The boost::function FAQ item 3 specifically addresses the scenario I am interested in:
Why are there workarounds for void
returns? C++ allows them! Void returns
are permitted by the C++ standard, as
in this code snippet:
void f();
void g() { return f(); }
This is a valid usage of
boost::function because void returns
are not used. With void returns, we
would attempting to compile ill-formed
code similar to:
int f();
void g() { return f(); }
In essence, not using void returns
allows boost::function to swallow a
return value. This is consistent with
allowing the user to assign and invoke
functions and function objects with
parameters that don't exactly match.
Unfortunately, this doesn't work in VS2008:
int Foo();
std::tr1::function<void()> Bar = Foo;
This produces errors starting with:
c:\Program Files\Microsoft Visual Studio 9.0\VC\include\xxcallfun(7) : error C2562: 'std::tr1::_Callable_fun<_Ty>::_ApplyX' : 'void' function returning a value
Is this a failing of the VS2008 TR1 implementation? Does this work in VS2010? Does TR1 address this capability? How about C++0x?
I believe tr1 addresses this issue. N1836 (the latest tr1 draft) says:
A function object f of type F is
Callable for argument types T1, T2,
..., TN and a return type R, if, given
lvalues t1, t2, ..., tNoftypesT1, T2,
..., TN,respectively,INVOKE(f, t1, t2,
..., tN)is well-formed([3.3]) and, if R
is not void, convertible to R.
In your example R is void, and so the last part of the requirements for Callable (convertible to R) is ignored.
However it looks like C++0x (C++11) changes the rules. In C++11 Callable is defined as INVOKE(f, t1, t2, ..., tN, R) which is defined in [func.require] as requiring INVOKE(f, t1, t2, ..., tN) to be implicitly convertible to R, with no exception for when R is void. So in C++11, your example should fail.