I'm trying to port some code written for GCC (8.2) to be compilable by Clang:
#include <tuple>
struct Q{};
using TUP = std::tuple<Q>;
template<typename Fn>
inline
void feh(Fn&, const std::tuple<>*)
{}
template<typename Fn, typename H>
inline
void feh(Fn& fn, const std::tuple<H>*)
{
fn(H{});
}
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
template<typename Tuple, typename Fn>
inline
void fe(Fn& fn, const Tuple * tpl = nullptr)
{
feh(fn, tpl);
}
int main()
{
auto r = [] (Q const&) {};
TUP tup;
fe<TUP>(r, &tup);
}
GCC 8.2 (and 12.1) compiles the code just fine. However, Clang 11.0.0 (and 14.0.0) complains that the call from fe to feh is ambiguous between void feh(Fn& fn, const std::tuple<H>*) [with Fn = (lambda at <source>:38:14), H = Q] and void feh(Fn& fn, const std::tuple<H, R...>*) [with Fn = (lambda at <source>:38:14), H = Q, R = <>].
https://godbolt.org/z/5E9M6a5c6
Which compiler is right?
How can I write this code so both compilers accept it?
Both if constexpr and fold expressions would work in C++17, but this is a library header included by many projects, and not all of them are compiled with C++17. I need a solution which works in C++11.
Which compiler is right?
Clang is wrong in rejecting the code because the first overload candidate feh(Fn& fn, const std::tuple<H>*) should be preferred over the other candidate feh(Fn& fn, const std::tuple<H, R...>*) since the former is more specialized than the latter.
In other words, the version without the pack is considered more specialized and hence should be preferred if it matches the call.
This is because, basically(roughly) for one function template to be considered more specialized than the other, the latter should be able to accept all the template arguments that the former can accept but not vice-versa.
Now, in your given example the overload feh(Fn& fn, const std::tuple<H, R...>*) can accept(or work with) all template arguments which the former feh(Fn& fn, const std::tuple<H>*) can accept but the reverse is not true. Hence the former is more specialized than the latter. For more technical details of this process, refer to What is the partial ordering procedure in template deduction or from [temp.deduct.partial]/10 which states:
Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.
(emphasis mine)
clang++ is correct because both functions matches equally good. I'm unsure which compiler that is correct, but...
A C++11 solution could be to just add the requirement that the Rest part must contain at least one type and that is easily done by just adding R1. That would mean that the rest of your code could be left unchanged:
template<typename Fn, typename H, typename R1, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R1, R...>*)
{
fn(H{});
using Rest = const std::tuple<R1, R...>*;
feh<Fn, R1, R...>(fn, static_cast<Rest>(nullptr));
}
A C++17 solution would be to remove the other feh overloads and use a fold expression:
template <typename Fn, typename... H>
inline void feh(Fn& fn, const std::tuple<H...>*) {
(..., fn(H{}));
}
This is a unary left fold over the comma operator which "unfolded" becomes:
(((fn(H1{}), fn(H2{})), ...), fn(Hn{}))
By far the simplest solution is an if constexpr :
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
if constexpr (sizeof...(R) > 0) {
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
}
and just remove the problematic overload.
How can I write this code so both compilers accept it?
You can write your code without recursion
template<typename Fn, typename ... Ts>
void fe(Fn& fn, const std::tuple<Ts...>* = nullptr)
{
// Trick to simulate fold expression of c++17
const int dummy[] = {0, (static_cast<void>(fn(Ts{})), 0)...};
static_cast<void>(dummy); // Avoid warning about unused variable
}
Which would become, in C++17
template<typename Fn, typename ... Ts>
void fe(Fn& fn, const std::tuple<Ts...>* = nullptr)
{
(static_cast<void>(fn(Ts{})), ...);
// static_cast is here to handle evil overloaded operator comma (for type returned by Fn)
// might be omitted if you know you are not in that pathological case
}
[Demo](fe(r, &tup);)
Related
I want to write a function for any number of parameters,
inline void show_log(const char* s)
{
std::cout << s << std::endl;
}
And I write it as
https://en.cppreference.com/w/cpp/language/parameter_pack instructs.
inline void show_log(const char* s)
{
std::cout << s << std::endl;
}
template<typename T, typename... Targs>
inline void show_log(T s, Targs ... args)
{
show_log(s);
show_log(args...);
}
inline void test()
{
show_log("abc", "ttt", "ccc");
}
It works fine. But I want more strict – make it only accepts const char* parameters. I tried this:
Declare a general template function (but not implement it.)
template<typename T, typename... Targs>
inline void show_log(T s, Targs ... args);
Then implement a specilization only for const char*
template<const char*, typename... Targs>
inline void show_log(const char* s, Targs ... args)
{
std::cout<<"show_log spelicalized for const char*\n"
show_log(s);
show_log(args...);
}
Call the function,
inline void test()
{
show_log("abc", "ttt", "ccc");
}
It didn't compile with LNK2019 `show_log(const char*, const char*, const char*)' didn't implement error.
The linker error that you're seeing is caused by the fact that you're creating an overload, not a specialization. Functions must be fully specialized since they cannot be overloaded. Classes are allowed to be partially specialized since they cannot be overloaded.
That said, you need to impose a constraint to your template arguments to achieve this behavior.
First, you need to define a meta-function:
// Returns true if all types are the same
// Returns false if any types are different
// Fails to compile if 0 or 1 arguments are passed in
template<typename first_t, typename ... rest_t>
struct is_all_same : std::conjunction<std::is_same<first_t, rest_t>...> {};
template<typename ... types_t>
constexpr auto is_all_same_v = is_all_same<types_t...>::value;
And then you can use the meta function in a number of ways to restrict your function usage.
// Using a static assert
template<typename T, typename... Targs>
inline void show_log(T s, Targs ... args)
{
static_assert(is_all_same_v<const char*, T, Targs...>, "Arguments must be of type: const char*");
show_log(s);
show_log(args...);
}
// requires clause is only available in c++20
template<typename T, typename... Targs> requires is_all_same_v<const char*, T, Targs...>
inline void show_log(T s, Targs ... args)
{
show_log(s);
show_log(args...);
}
// Use SFINAE (do yourself a favor and use C++ requires instead of this if it's available to you)
template<typename T, typename... Targs>
inline std::enable_if_t<is_all_same_v<const char*, T, Targs...>> show_log(T s, Targs ... args)
{
show_log(s);
show_log(args...);
}
One thing to note, is that you appear to be using recursion to accomplish this. However, you do not need to use recursion here if you have C++17 available (which I assume you do, given the tag). I would recommend doing something like this for c++17:
inline void show_log(const char* s)
{
std::cout << s << std::endl;
}
// No recursion. Be nice to your compiler when you can
template<typename ... args_t>
inline auto show_log(args_t ... args)
{
static_assert(is_all_same_v<const char*, args_t...>, "Arguments to show_log must have the type: const char*");
(show_log(args), ...);
}
If you are going to be using variadics (and templates in general), I would highly recommend keeping the above meta function handy. I use it everywhere in my template code. Understand it and digest it, so you can add more meta functions like it to your tool belt.
Edit: As Keijo excellently points out below, you may actually be interested in the single-function implementation of your pattern. Combining all of the recommendations on this thread yields you with something like this:
// Returns true if all types are the same
// Returns false if any types are different
// Fails to compile if 0 or 1 arguments are passed in
template<typename first_t, typename ... rest_t>
struct is_all_same : std::conjunction<std::is_same<first_t, rest_t>...> {};
template<typename ... types_t>
constexpr auto is_all_same_v = is_all_same<types_t...>::value;
template<typename ... args_t>
auto show_log(args_t ... args) noexcept -> void
{
using wanted_type = const char*;
static_assert(is_all_same_v<wanted_type, args_t...>, "Arguments must be of type: const char*");
((std::cout << args << '\n'), ...) << std::flush;
}
This solution:
Is defined by a single variadic function
Is C++17 compatible
Throws a compile error if any of the arguments are not of type const char*
Allows you to reuse is_all_same_v<args_t...> wherever you see fit
Gives a useful compiler error
Avoids unnecessary flushes by using '\n' rather than std::endl
Removes the inline keyword, since this is a function template (which natively has the same semantics as an inline function and is therefore redundant... Note that this does not apply to full specializations of function templates, since specializations are not templates)
If you don't need endl between the arguments, you can do without specialization.
using WantedType = const char *;
template <class... Args>
std::enable_if_t<std::conjunction_v<std::is_same<WantedType, Args>...>>
show_log(Args... args)
{
(std::cout << ... << args) << std::endl;
}
Can anyone think of how to add linefeeds into this solution without specialization? Maybe it is not possible?
--- EDIT ---
As Christopher points out in comments, there is a neat single function solution:
using WantedType = const char *;
template <class... Args>
std::enable_if_t<std::conjunction_v<std::is_same<WantedType, Args>...>>
show_log(Args... args)
{
((std::cout << args << '\n'), ...) << std::flush;
}
In order to really understand C++17 fold expression, I have written a function append for containers :
#include <iostream>
#include <vector>
#include <list>
// fold expression
template<typename T, typename U, template<typename> typename... Args>
inline void append_impl(std::vector<T>& v, Args<U>... args) noexcept
{
static_assert((std::is_constructible_v<T,U>));
std::cout << "append_impl version one " << std::endl;
(v.insert(std::end(v),
std::begin(args),
std::end (args)), ...);
}
//fold expression
template<typename T, typename... Args>
inline void append_impl(std::vector<T>& v, Args&&... args) noexcept
{
static_assert((std::is_constructible_v<T, Args&&> && ...));
std::cout << "append_impl version two " << std::endl;
(v.push_back(std::forward<Args>(args)), ...);
}
// fold expression
template<typename T, typename... Args>
inline void append(std::vector<T>& v, Args&&... args) noexcept
{
(append_impl(v, args), ...);
}
int main()
{
std::vector<int> a = {1,2};
std::vector<int> b = {3,4};
std::vector<int> c = {5,6};
std::list<int> d = {15,16};
append(a,b,c, std::vector<int>{8,9}, 10, 11, 12, 13, 14, d);
for(const auto& e : a)
{
std::cout << e << " ";
}
std::cout << std::endl;
return 0;
}
this works fine and give me the result :
append_impl version one
append_impl version one
append_impl version one
append_impl version two
append_impl version two
append_impl version two
append_impl version two
append_impl version two
append_impl version one
1 2 3 4 5 6 8 9 10 11 12 13 14 15 16
But I have a question from this code :
In the first version of append_impl, args is pass by copy. I wanted to use universal references (in Scott Meyers sens) to avoid this, but append_impl(std::vector<T>& v, Args<U>&&... args) give me a compilation error.
rog.cc: In instantiation of 'void append_impl(std::vector<T>&, Args&& ...) [with T = int; Args = {std::vector<int, std::allocator<int> >&}]':
prog.cc:18:15: required from 'void append(std::vector<T>&, Args&& ...) [with T = int; Args = {std::vector<int, std::allocator<int> >&, std::vector<int, std::allocator<int> >&, std::vector<int, std::allocator<int> >, int, int, int, int, int, std::__cxx11::list<int, std::allocator<int> >&}]'
prog.cc:29:59: required from here
prog.cc:10:3: error: static assertion failed
10 | static_assert((std::is_constructible_v<T, Args&&> && ...));
| ^~~~~~~~~~~~~
prog.cc:12:15: error: no matching function for call to 'std::vector<int>::push_back(std::vector<int>&)'
12 | (v.push_back(std::forward<Args>(args)), ...);
Why and what can I do to avoid copy ?
Following issues with your code:
You forgot to #include<type_traits>.
You forgot to forward in append:
(append_impl(v, std::forward<Args>(args)), ...);
Your template template parameter in append_impl is a bit problematic. Because it is written as template<typename> typename... Args it assumes that the template passed to it takes one template parameter. std::list (and other containers) take multiple template arguments (although the other ones are defaulted).It is not entirely clear whether such a template should be valid for your template template parameter.
GCC accepts it, while Clang doesn't (see https://godbolt.org/z/LY9r-k). I think that after resolution of CWG issue 150 GCC is correct in accepting the code, but I haven't checked in detail.
In any case, this problem can be easily avoided by changing the parameter to template<typename...> typename... Args, which accepts templates with arbitrarily many parameters.
Your actual issue: Universal references (or forwarding references) do only work if a template parameter T is directly used as T&& in a function parameter. If you use it inside a function parameters' template argument, the rules for reference collapse resulting in the forwarding behavior of universal references do not apply.
Args<U>&&... args is always a rvalue reference, no matter what reference qualification U has.
Therefore your code does not compile if you try to forward a lvalue reference to this function template which always expects a rvalue reference.
You should probably write one template overload for rvalue references and one for const lvalue references:
template<typename T, typename U, template<typename...> typename... Args>
inline void append_impl(std::vector<T>& v, const Args<U>&... args) noexcept
template<typename T, typename U, template<typename...> typename... Args>
inline void append_impl(std::vector<T>& v, Args<U>&&... args) noexcept
Then you have however the problem that a non-const lvalue reference will be a better match for the second template overload taking Args&& directly, so you need to add another overload:
template<typename T, typename U, template<typename...> typename... Args>
inline void append_impl(std::vector<T>& v, Args<U>&... args) noexcept
{
append_impl(v, std::as_const(args)...);
}
(requires #include<utility>)
In general the way you are trying to identify containers is not save. It will fail for all types that are formed from template classes and are not containers.
Instead you could select the correct function overload by SFINAE on the container interface that you are going to use, i.e. std::begin(arg), std::end(arg) and the insert call. You can use expression SFINAE to do so.
Alternatively you can write a type trait for the well-formedness of these expressions and use a single overload for append_impl that chooses the implementation based on a if constexpr checking the type trait.
In C++20 this will be doable in a much simpler way, using concepts.
There also doesn't seem to be any reason that append_impl takes Args as a parameter pack. It is only ever called with one parameter for Args.
My compiler doesn't support if constexpr, but I am captivated by its benefit.
I must have it - even it might be a fake one.
This code is my attempt to mimic if constexpr behavior.
The objective is to make the lines (###) appear in only 1 function :-
#include <iostream>
using namespace std;
template<bool Flag,typename F> constexpr typename std::enable_if<!Flag, void>::type iter_(F f,int i1){
f(i1); //No! The compiler still tried to compile even Flag=true
}
template<bool Flag,typename F> constexpr typename std::enable_if<Flag, void>::type iter_(F f,int i1){ }
template<bool Flag,typename F> constexpr typename std::enable_if<Flag, void>::type iter_(F f,int i1,int i2){
f(i1,i2); //No! The compiler still tried to compile even Flag=false
}
template<bool Flag,typename F> constexpr typename std::enable_if<!Flag, void>::type iter_(F f,int i1,int i2){}
template<bool Flag,typename F> constexpr void fff( F f ){
for(int n=0;n<5;n++){//fake loop, the real situation is very complex
//### some horror code appeared here, but omitted
if(Flag){//attempt to mimic "if constexpr"
iter_<true>(f,1,2);
}else{
iter_<false>(f,3);
}
}
}
This is its usage:-
template<typename F> constexpr void fff1( F f ){fff<false>(f);} //usage
template<typename F> constexpr void fff2( F f ){fff<true>(f);} //usage
int main() {
// your code goes here
auto f1=[&](int a){
cout<<a<<" ";
};
auto f2=[&](int a,int b){
cout<<a<<" "<<b<<endl;
};
fff1(f1);
fff2(f2);
return 0;
}
I got compile error :
prog.cpp: In instantiation of 'constexpr typename std::enable_if<Flag, void>::type iter_(F, int, int) [with bool Flag = true; F = main()::<lambda(int)>; typename std::enable_if<Flag, void>::type = void]':
prog.cpp:16:18: required from 'constexpr void fff(F) [with bool Flag = false; F = main()::<lambda(int)>]'
prog.cpp:22:61: required from 'constexpr void fff1(F) [with F = main()::<lambda(int)>]'
prog.cpp:33:9: required from here
prog.cpp:9:3: error: no match for call to '(main()::<lambda(int)>) (int&, int&)'
f(i1,i2);
^
prog.cpp:9:3: note: candidate: void (*)(int) <conversion>
prog.cpp:9:3: note: candidate expects 2 arguments, 3 provided
From the error, it is clear to me that even when a function has std::enable_if[ effective FALSE],
the compiler still compiled code that inside the function.
- That is bad.
Which parts do I have to edit?
... or are there any alternatives?
... or is it not possible to mimic if constexpr at all (that is the reason why it is introduced finally)?
Read this: http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0128r1.html
Can't we do this with existing language features?
John Spicer suggested in c++std-ext-17099 that polymorphic lambdas
combined with a decision-making template would provide an adequate
facility without a need to add new language features. The invocation
of that decision-making template looks roughly like this:
template <int arg, typename ... Args> int do_something(Args... args) {
return static_if<sizeof...(args)>::get(
[](auto x, auto y) { return x+y; },
[](auto x) { return *x; })(args...);
}
Now, in comparison, with the proposed language facility, we do
template <int arg, typename ... Args> int do_something(Args... args) {
constexpr if (sizeof...(args)) {
return (args + ...);
} constexpr_else {
return *args...;
}
}
Now this is sort of a an alternative. It gets more complicated if the different branches return different types.
Moreover,
I must point out some things here:
I can return, break, continue and goto from within a constexpr if
block. I can not do that from within the lambda.
While I am a big
proponent of using lambdas to create new control facilities, I find
the constexpr if solution infinitely more readable.
Further,
Richard Smith explained the following:
Right, when a function template is instantiated, all of the
declarations/statements/expressions within it are instantiated, and
that includes pieces inside local classes, generic lambdas, and so on.
This instantiation of generic lambda bodies is in fact necessary for
our language semantics -- computing the captures of a generic lambda
within a function template specialization relies on us having already
instantiated the complete closure type and its call operator template
to the point that we know where the odr-uses are within the
non-dependent full-expressions within the body.
In contrast, the intent of constexpr if is that the branch not taken
is not instantiated.
I'm working on a project which involves providing an interface for users to find optima of functions of arbitrary numbers of arguments. Internally, all the mechanism is built around std::tuples of the argument types. I want to provide users the ability to call my optimization routines, though, on functions written in the "usual" style (such as f1 in the example), rather than having to write their functions to be optimized as functions of std::tuple instantiations (such as f2 in the example).
As part of this mechanism, I have written an apply function which unpacks a tuple into the arguments of a given function and calls it.
I have also created a pair of function templates, one forwarding to the other with a lambda wrapper, providing the interface to the optimization routine. A simplified version appears below as tuple_array_map. The intention was to provide SFINAE for selection between the two, depending on whether the function type is callable with a tuple argument, or callable with the unpacked tuple members as arguments. I use dummy template parameters with SFINAE-triggering default arguments for this purpose.
This scheme works perfectly under g++ 4.7 and higher and compiling with -std=c++11 -pedantic -Wall -Wextra -Werror produces no warnings or errors.
However, when trying to compile under clang 5.1 with -std=c++11 (sorry, I'm not a big clang user and I don't know if there's a more appropriate set of options), I get the following output for my example code:
clang_fail.cpp:91:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map(f2, tuples);
^~~~~~~~~~~~~~~
clang_fail.cpp:59:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:71:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map([&](const typename TupleArr::value_type& t) {
^~~~~~~~~~~~~~~
clang_fail.cpp:90:5: note: in instantiation of function template specialization
'tuple_array_map<double (*)(double),
std::__1::array<std::__1::tuple<double>, 5>, double, void>' requested here
tuple_array_map(f1, tuples);
^
clang_fail.cpp:59:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
The really puzzling part is that it appears to deduce a double return from a call expression that should SFINAE out, unless I've missed something from the standard regarding either template default arguments or SFINAE itself.
Example follows---it's as minimal as I could get it while still triggering the same behavior:
#include <tuple>
#include <array>
#include <utility>
#include <type_traits>
double f1(double x)
{
return x * 2;
}
double f2(const std::tuple<double>& x)
{
return std::get<0>(x) * 2;
}
template<std::size_t N>
struct apply_impl {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&& t, TParams&&... args)
-> decltype(
apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
))
{
return apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
);
}
};
template<>
struct apply_impl<0> {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&&, TParams&&... args)
-> decltype(std::forward<F>(fn)(std::forward<TParams>(args)...))
{
return std::forward<F>(fn)(std::forward<TParams>(args)...);
}
};
template<class F, class Tuple>
auto apply(F&& fn, Tuple&& t)
-> decltype(apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t)))
{
return apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t));
}
template<class Fn, class TupleArr,
class = decltype(std::declval<Fn>()(
std::declval<typename TupleArr::value_type>()))>
void tuple_array_map(Fn f, const TupleArr& arr)
{
for (auto i = 0; i < arr.size(); ++i)
static_cast<void>(f(arr[i]));
}
template<class Fn, class TupleArr,
class = decltype(apply(std::declval<Fn>(),
std::declval<typename TupleArr::value_type>())),
class = void>
void tuple_array_map(Fn f, const TupleArr& arr)
{
tuple_array_map([&](const typename TupleArr::value_type& t) {
return apply(f, t);
}, arr);
}
int main()
{
std::array<std::tuple<double>, 5> tuples = {
std::make_tuple(1),
std::make_tuple(2),
std::make_tuple(3),
std::make_tuple(4),
std::make_tuple(5)
};
// "apply" unpacks a tuple into arguments to a function
apply(f1, tuples[0]);
// this call produces an ambiguity one level down under clang
tuple_array_map(f1, tuples);
// this call directly produces an ambiguity under clang
tuple_array_map(f2, tuples);
}
The ambiguity when compiling with libc++ is due to the lack of the standard-mandated explicit specifier on std::tuple's converting constructor (Constructor #2 at cppreference). Consequently, double is implicitly convertible to std::tuple<double> (See this example program) so both of your tuple_apply_map functions are viable.
As a workaround, I suggest creating a needs_apply trait and using that to constrain your tuple_apply_map templates (I'll use tag dispatching):
template<class Fn, class TupleArr>
struct needs_apply {
template <class F=Fn>
static auto test(int) ->
decltype(std::declval<F>()(*std::declval<TupleArr>().begin()), std::false_type{});
static auto test(...) -> std::true_type;
using type = decltype(test(0));
};
template<class Fn, class TupleArr>
void tuple_array_map(Fn f, const TupleArr& arr, std::false_type)
{
for (auto&& i : arr)
static_cast<void>(f(i));
}
template<class Fn, class TupleArr>
void tuple_array_map(Fn f, const TupleArr& arr, std::true_type)
{
tuple_array_map([&](const typename TupleArr::value_type& t) {
return apply(f, t);
}, arr, std::false_type{});
}
template<class Fn, class TupleArr>
void tuple_array_map(Fn&& f, TupleArr&& arr) {
tuple_array_map(std::forward<Fn>(f), std::forward<TupleArr>(arr),
typename needs_apply<Fn,TupleArr>::type{});
}
This works correctly with libc++ and with libstdc++ and even compiling with g++.
According to this answer by Howard Hinnant, this non-conformance of the std::tuple constructor is an extension implemented in libc++ as an experiment.
See also Library Working Group active issue 2051 and the paper N3680 written by Daniel Krügler to address the issue.
I'm trying to write a simple function to convert a std::function<> object while binding the last parameter(s). That's what I've got:
template<typename R, typename Bind, typename ...Args> std::function<R (Args...)> bindParameter (std::function<R (Args..., Bind)> f, Bind b)
{
return [f, b] (Args... args) -> R { return f (args..., b); };
}
And that's how I'd like to use it:
int blub (int a, int b)
{
return a * b;
}
// ...
int main ()
{
std::function<int (int, int)> f1 (blub);
// doesn't work
std::function<int (int)> f2 = bindParameter (f1, 21);
// works
std::function<int (int)> f3 = bindParameter<int, int, int> (f1, 21);
return f2 (2);
}
... so that in this example the main function should return 42. The problem is, that gcc (4.6) doesn't seem to infer the types of the template parameters correctly, the first version produces the following errors:
test.cpp:35:58: error: no matching function for call to 'bindParameter(std::function<int(int, int)>&, int)'
test.cpp:35:58: note: candidate is:
test.cpp:21:82: note: template<class R, class Bind, class ... Args> std::function<R(Args ...)> bindParameter(std::function<R(Args ..., Bind)>, Bind)
But in my opinion the parameters are obvious. Or is this kind of type inference not covered by the standard or not yet implemented in gcc?
You can't use std::function as a deduced parameter of a function template. Deduction can't work in this fashion as there are no rules to match int(*)(int, int) to std::function<int(int, int)>. (Consider also that for any std::function<Signature> there is a constructor accepting int(*)(int, int), even if in most cases this results in an error when instantiated.)
It's problematic to detect the signature of functor in the general case. Even KennyTM's solution has limitations: it detects the signature of monomorphic functors and function-like things, but won't work for polymorphic functors (e.g. with overloaded operator()) or functors with surrogate call functions (even in the monomorphic case).
It is however possible to completely sidestep the issue of detecting the signature thanks to decltype (or equivalently, std::result_of), and I would recommend doing so. Hence, a variant on KennyTM's answer:
template<typename Functor, typename Bound>
struct bind_last_type {
Functor functor;
Bound bound;
template<typename... Args>
auto operator()(Args&&... args)
-> typename std::result_of<Functor&(Args..., Bound)>::type
// equivalent:
// -> decltype( functor(std::forward<Args>(args)..., std::move(bound)) )
{ return functor(std::forward<Args>(args)..., std::move(bound)); }
};
template<typename Functor, typename Bound>
bind_last_type<
typename std::decay<Functor>::type
, typename std::decay<Bound>::type
>
bind_last(Functor&& functor, Bound&& bound)
{ return { std::forward<Functor>(functor), std::forward<Bound>(bound) }; }
Not sure about the inference, but it works if I just define a templated function object.
template <typename FType, typename LastArgType>
struct BindLastHelper
{
FType _f;
LastArgType _last_arg;
template <typename... Args>
typename utils::function_traits<FType>::result_type
operator()(Args&&... args) const
{
return _f(std::forward<Args>(args)..., _last_arg);
}
};
template<typename FType, typename LastArgType>
BindLastHelper<FType, LastArgType> bindParameter (FType f, LastArgType b)
{
return BindLastHelper<FType, LastArgType>{f, b};
}
Note:
utils::function_traits is taken from https://github.com/kennytm/utils/blob/master/traits.hpp. std::result_of cannot be used because you are not passing a function pointer.
Proof of concept: http://ideone.com/ux7YY (here for simplicity I just redefined result_of.)