When I use have template function which accepts another function as a parameter, C++ can't derive template parameters. It's very annoying to specify them all the time. How can I define the following function such that I don't have to specify type parameters every time?
#include <functional>
template <typename S, typename T>
T apply(const S& source, const function<T (const S&)>& f) {
return f(source);
}
template <typename S, class Functor, typename T>
T applyFun(const S& source, const Functor& f) {
return f(source);
}
int main() {
// Can't derive T. Why?
apply(1, [](int x) { return x + 1; });
// Compiles
apply<int, int>(1, [](const int& x) { return x + 1; });
// Can't derive T. Kind of expected.
applyFun(1, [](int x) { return x + 1; });
}
It makes sense to me why it can't derive type parameter in the second function, but not in the first one (since x + 1 is int, so it should deduce that T = int).
A template parameter must appear in a function parameter type to be deductible. Moreover lambdas are not functions so, whatsoever the return type of a lambda cannot participate to template argument deduction.
But in this case, there is no need to specify the return type. Return type deduction can do the job:
template <typename S, class Functor>
auto applyFun(const S& source, const Functor& f) {
return f(source);
}
If you can use C++17, you can use the deduction guides for std::function as follows
template <typename S, typename F,
typename T = typename decltype( std::function{std::declval<F>()} )::result_type>
T applyFun (S const & source, F const & f)
{
return f(source);
}
but, as pointed by Oliv, for your example function there is non need of T because you can use auto (from C++14; auto ... -> decltype(f(source)) in C++11).
-- EDIT --
The OP say
The good thing about this solution is that I can use T inside the function (e.g. if I want to implement vector_map).
You can detect and use T, also inside the function, using a using
Something as
template <typename S, typename F>
auto applyFun (S const & source, F const & f)
{
using T = typename decltype( std::function{f} )::result_type;
return f(source);
}
or simpler: using T = decltype( f(source) );.
The OP also observe that
The downside is that for some reason now I can't write [] (const auto& x) { ... } in function call.
Correct.
Because std::function template types can't be deduced from a generic-lambda.
But using the fact that you know the type of the argument, you can use decltype() again
template <typename S, typename F,
typename T = decltype(std::declval<F const>()(std::declval<S const>()))>
T applyFun (S const & source, F const & f)
{ return f(source); }
This solution should works also for C++14 and C++11.
Related
Say I have a tuple of types T1,...,TN that implement some method, apply().
How do I define a function that takes this tuple and some initial element, and returns the chained call of apply() on this element?
For example:
template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
// return ???
}
// simple example
struct Sqr {
static int apply(int x) { return x * x; }
};
enum class Choice {
One,
Two,
};
struct Choose {
static int apply(Choice choice) {
switch (choice) {
case Choice::One:
return 1;
case Choice::Two:
return 2;
}
}
};
void test() {
auto tpl = std::tuple(Sqr{}, Choose{});
assert(apply(tpl, Choice::One) == 1);
assert(apply(tpl, Choice::Two) == 4);
}
I tried to use fold expressions, and variations of answers from: Template tuple - calling a function on each element but couldn't get anything to compile.
The main difference is that I need each invocation's result as the input for the next one.
Concretely, I tried the following, which failed because it calls each argument with the initial value:
template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto &&... args) {
return (..., args.apply(x));
}, tpl);
}
Clarifications and assumptions:
I want the methods to be called in a specific order - last to first - similarly to mathematical function composition.
(f * g)(x) := f(g(x))
The input and output types of each tuple argument are not constricted. The only assumption is that consecutive arguments agree on the corresponding types.
There may be snazzier C++17 ways of doing it, but there is always good old-fashioned partially-specialized recursion. We'll make a struct that represents your recursive algorithm, and then we'll build a function wrapper around that struct to aid in type inference. First, we'll need some imports.
#include <tuple>
#include <utility>
#include <iostream> // Just for debugging later :)
Here's our structure definition.
template <typename Input, typename... Ts>
struct ApplyOp;
Not very interesting. It's an incomplete type, but we're going to provide specializations. As with any recursion, we need a base case and a recursive step. We're inducting on the tuple elements (you're right to think of this as a fold-like operation), so our base case is when the tuple is empty.
template <typename Input>
struct ApplyOp<Input> {
Input apply(Input x) {
return x;
}
};
In this case, we just return x. Computation complete.
Now our recursive step takes a variable number of arguments (at least one) and invokes .apply.
template <typename Input, typename T, typename... Ts>
struct ApplyOp<Input, T, Ts...> {
auto apply(Input x, const T& first, const Ts&... rest) {
auto tail_op = ApplyOp<Input, Ts...>();
return first.apply(tail_op.apply(x, rest...));
}
};
The tail_op is our recursive call. It instantiates the next version of ApplyOp. There are two apply calls in this code. first.apply is the apply method in the type T; this is the method you control which determines what happens at each step. The tail_op.apply is our recursive call to either another version of this apply function or to the base case, depending on what Ts... is.
Note that we haven't said anything about tuples yet. We've just taken a variadic parameter pack. We're going to convert the tuple into a parameter pack using an std::integer_sequence (More specifically, an std::index_sequence). Basically, we want to take a tuple containing N elements and convert it to a sequence of parameters of the form
std::get<0>(tup), std::get<1>(tup), ..., std::get<N-1>(tup)
So we need to get an index sequence from 0 up to N-1 inclusive (where N-1 is our std::tuple_size).
template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
// ???
}
That complicated-looking type alias is building our index sequence. We take the tuple's size (std::tuple_size<std::tuple<Ts...>>::value) and pass it to std::make_index_sequence, which gives us an std::index_sequence<0, 1, 2, ..., N-1>. Now we need to get that index sequence as a parameter pack. We can do that with one extra layer of indirection to get type inference.
template <typename Input, typename... Ts, std::size_t... Is>
auto apply(const std::tuple<Ts...>& tpl, Input x, std::index_sequence<Is...>) {
auto op = ApplyOp<Input, Ts...>();
return op.apply(x, std::get<Is>(tpl)...);
}
template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
return apply(tpl, x, seq());
}
The second apply is the one outside users call. They pass a tuple and an input value. Then we construct an std::index_sequence of the appropriate type and pass that to the first apply, which uses that index sequence to access each element of the tuple in turn.
Complete, runnable example
The main difference is that I need each invocation's result as the
input for the next one.
Apply fold-expression to assignment operator
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto... op) {
return ((x = op.apply(x)), ...);
}, tpl);
}
Demo
You can introduce an dummy variable for reverse order
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto... op) {
int dummy;
(dummy = ... = ((x = op.apply(x)), 0));
return x;
}, tpl);
}
Demo
One way without recursion is to use fold expression.
Unfortunately, there is no call composition operator folding.
But you might create custom type and divert regular operator:
template <typename T>
struct Wrapper
{
T t;
};
// Deduction guide, not needed in C++20
template <typename T> Wrapper(T) -> Wrapper<T>;
// Then the operator with changed semantic
template <typename T1, typename T2>
auto operator+(const Wrapper<T1>& lhs, const Wrapper<T2>& rhs)
{
return Wrapper{lhs.t.apply(rhs.t)};
}
template <typename T1, typename T2>
auto operator-(const Wrapper<T1>& lhs, const Wrapper<T2>& rhs)
{
return Wrapper{rhs.t.apply(lhs.t)};
}
// And now, the function with fol expression
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tup, Input x) {
return std::apply([&](auto&...args){
return (Wrapper<const Args&>{args} + ... + Wrapper<Input&>{x});
}, tup).t;
}
template <typename... Args, typename Input>
auto my_apply_rev(std::tuple<Args...> const &tup, Input x) {
return std::apply([&](auto&...args){
return (Wrapper<Input&>{x} - ... - Wrapper<const Args&>{args});
}, tup).t;
}
Usage similar to
// std::size(std::to_string(10 * 10));
my_apply(std::tuple{ LengthOp{}, ToStringOp{}, SquareOp{}}, 10);
my_apply_rev(std::tuple{ SquareOp{}, ToStringOp{}, LengthOp{}}, 10);
Demo
If I have a tuple member variable where the types in the tuple are a parameter pack of a class template, I can apply a function to each object in the tuple with a static member function like this:
template<size_t I = 0, typename F, typename... Tp>
static void apply_to_foos(std::tuple<Tp...>& t, F func) {
auto& foo = std::get<I>(t);
func(foo);
if constexpr (I + 1 != sizeof...(Tp))
apply_to_foos<I + 1>(t, func);
}
so for example, if foos_is the tuple member variable, I could implement a member function over the foos via:
void frobnicate() {
apply_to_foos( foos_, [](auto& f){f.frob();} );
}
etc. however, if frobnicate had been const I run into the problem that foos_ will now be const, and apply_to_foos wants a non-const tuple. So for example this won't work (if foos are some kind of containers)
size_t size() const {
size_t sz = 0;
apply_to_foos( foos_, [&sz](auto& f){ sz += f.size();} );
return sz;
}
I could implement an overload of apply_to_foos that takes a const std::tuple<Tp...>&, const_cast to a non-const tuple and call the original overload, but, well, then I am just casting away const-ness.
The annoying thing about it is that the only part of apply_to_foos that cares about const-ness is the signature. Everything else will work with const values as long the const-ness is respected e.g. the lambda will need to take a reference to const value, etc. If I could cast from a const tuple to a tuple of consts then I could implement the const overload like this:
template<size_t I = 0, typename F, typename... Tp>
static void apply_to_foos(const std::tuple<Tp...>& t, F func) {
auto& tup_of_consts = const_tuple_to_tuple_of_consts(t);
apply_to_foos(tup_of_consts, func);
}
and the size() member function would just work naturally ... I feel like there must be an easier way to handle this though?
You can't cast a const std::tuple<T1, T2> to std::tuple<const T1, const T2> without making copies of the contained items.
You have two options for what you can do though:
1. Deduce the type of t and sidestep the problem entirely:
template<size_t I = 0, typename F, typename T>
static void apply_to_foos(T& t, F func) {
auto& foo = std::get<I>(t);
func(foo);
if constexpr (I + 1 != std::tuple_size<T>::value)
apply_to_foos<I + 1>(t, func);
}
Live Demo
With the type of t deduced, both situations will work. If passed a const tuple then T will be decuded to const std::tuple<...> making the type of t const std::tuple<...>& and if passed a non-const tuple then T will be deduced to std::tuple<...> and the type of t will be std::tuple<...>&. The only other change required is to use std::tuple_size<T>::value in place of sizeof...(Tp). Note that this also allows apply_to_foos to work with other types like std::array and std::pair.
You'll probably also want to apply perfect-forwarding to allow apply_to_foos to work with rvalues and preserve the value category of the tuple elements as well. For example:
template<size_t I = 0, typename F, typename T>
static void apply_to_foos(T&& t, F func) {
func(std::get<I>(std::forward<T>(t)));
if constexpr (I + 1 != std::tuple_size<std::remove_reference_t<T>>::value)
apply_to_foos<I + 1>(std::forward<T>(t), func);
}
Live Demo
2. Create a new tuple containing references to the original tuple's contents
Create a second overload of apply_to_foos that accepts a reference to a const tuple and creates a temporary tuple of const references to the original tuple's elements:
template<typename F, typename... Tp>
static void apply_to_foos(const std::tuple<Tp...>& t, F func) {
std::tuple<std::add_const_t<Tp>&...> tuple_of_const = t;
apply_to_foos(tuple_of_const, func);
}
Live Demo
This will work fine, but it has issues with value category preservation. The elements of a tuple rvalue will be passed to the callback function as const lvalues and can't easily be moved-from for example.
I think this is an okay use of const_cast to pretend that your mutable parameter is const. It may be UB, I'm no expert.
Basically, put the logic in the const version for const-correctness. Then the non-const version calls the const version after casting away the const from the reference.
First, removing a const from a reference (might be a better way to do this):
// badly named but turns `const int &` -> `int &`
template <typename T>
struct remove_const_ref {
using type = std::add_lvalue_reference_t<
std::remove_const_t<std::remove_reference_t<T>>>;
};
template <typename T>
using remove_const_ref_t = typename remove_const_ref<T>::type;
Then, the const version:
template <size_t I = 0, typename F, typename... Tp>
static void apply_to_foos(const std::tuple<Tp...> &t, F func) {
auto &foo = std::get<I>(t);
func(foo);
if constexpr (I + 1 != sizeof...(Tp)) apply_to_foos<I + 1>(t, func);
}
And the non-const version, which just casts and calls:
template <size_t I = 0, typename F, typename... Tp>
static void apply_to_foos(std::tuple<Tp...> &t, F func) {
apply_to_foos(std::as_const(t), [&func](const auto &v) {
func(const_cast<remove_const_ref_t<decltype(v)>>(v));
});
}
Example:
int main() {
std::tuple thingMut = {1, 2, 3, 4};
const std::tuple thingConst = {1, 2, 3, 4};
apply_to_foos(thingMut, [](auto &v) {
v++;
std::cout << "mut frob: " << v << std::endl;
});
apply_to_foos(thingConst, [](auto &v) {
// v++; error: you cannot assign to a variable that is const
std::cout << "const frob: " << v << std::endl;
});
}
Suppose I have a variable constructors, which is a tuple of constructor functions represented in variadic generic lambdas.
// types for constructors
using type_tuple = std::tuple<ClassA, ClassB, ClassC>;
// Get a tuple of constructors(variadic generic lambda) of types in type_tuple
auto constructors = execute_all_t<type_tuple>(get_construct());
// For definitions of execute_all_t and get_construct, see link at the bottom.
I can instantiate an object with:
// Create an object using the constructors, where 0 is index of ClassA in the tuple.
ClassA a = std::get<0>(constructors)(/*arguments for any constructor of ClassA*/);
Is it possible to index the type in runtime with a magic_get like below?
auto obj = magic_get(constructors, 0)(/*arguments for any constructor of ClassA*/);
// Maybe obj can be a std::variant<ClassA, ClassB, ClassC>, which contains object of ClassA?
Edit: Ideally obj should be an instance of ClassA. If not possible, I can accept obj to be std::variant<ClassA, ClassB, ClassC>.
Please check out the minimal reproducible example: Try it online!
A similar question: C++11 way to index tuple at runtime without using switch
.
You might have your runtime get return std::variant, something like:
template <typename ... Ts, std::size_t ... Is>
std::variant<Ts...> get_impl(std::size_t index,
std::index_sequence<Is...>,
const std::tuple<Ts...>& t)
{
using getter_type = std::variant<Ts...> (*)(const std::tuple<Ts...>&);
getter_type funcs[] = {+[](const std::tuple<Ts...>& tuple)
-> std::variant<Ts...>
{ return std::get<Is>(tuple); } ...};
return funcs[index](t);
}
template <typename ... Ts>
std::variant<Ts...> get(std::size_t index, const std::tuple<Ts...>& t)
{
return get_impl(index, std::index_sequence_for<Ts...>(), t);
}
Then you might std::visit your variant to do what you want.
Demo
or for your "factory" example:
int argA1 = /*..*/;
std::string argA2 = /*..*/;
int argB1 = /*..*/;
// ...
auto obj = std::visit(overloaded{
[&](const A&) -> std::variant<A, B, C> { return A(argA1, argA2); },
[&](const B&) -> std::variant<A, B, C> { return B(argB1); },
[&](const C&) -> std::variant<A, B, C> { return C(); },
}, get(i, t))
This can probably be done more nicely, but here is an attempt according to your requirements in the comments.
Requires C++17, works on Clang, but gives an Internal Compiler Error on GCC.
It does require though, that you make the constructing function SFINAE-friendly, otherwise there is no way of checking whether it can be called:
So use
return [](auto... args) -> decltype(U(args)...) { return U(args...); };
instead of
return [](auto... args) { return U(args...); };
The behavior of this function given arguments tup and index is as follows:
It returns a lambda that when called with a list of arguments will return a std::variant of all the types that could result from calls of the form std::get<i>(tup)(/*arguments*/). Which one of these is actually called and stored in the returned variant is decided at runtime through the index argument. If index refers to a tuple element that cannot be called as if by std::get<index>(tup)(/*arguments*/), then an exception is thrown at runtime.
The intermediate lambda can be stored and called later. Note however that it saves a reference to the tup argument, so you need to make sure that the argument out-lives the lambda if you don't call and discard it immediately.
#include <tuple>
#include <type_traits>
#include <variant>
#include <utility>
#include <stdexcept>
template<auto V> struct constant_t {
static constexpr auto value = V;
using value_type = decltype(value);
constexpr operator value_type() const {
return V;
}
};
template<auto V>
inline constexpr auto constant = constant_t<V>{};
template<auto V1, auto V2>
constexpr auto operator+(constant_t<V1>, constant_t<V2>) {
return constant<V1+V2>;
}
template<typename T>
struct wrap_t {
using type = T;
constexpr auto operator+() const {
return static_cast<wrap_t*>(nullptr);
}
};
template<typename T>
inline constexpr auto wrap = wrap_t<T>{};
template<auto A>
using unwrap = typename std::remove_pointer_t<decltype(A)>::type;
template <typename Tup>
auto magic_get(Tup&& tup, std::size_t index) {
return [&tup, index](auto&&... args) {
// Get the input tuple size
constexpr auto size = std::tuple_size_v<std::remove_const_t<std::remove_reference_t<Tup>>>;
// Lambda: check if element i of tuple is invocable with given args
constexpr auto is_valid = [](auto i) {
return std::is_invocable_v<decltype(std::get<i>(tup)), decltype(args)...>;
};
// Lambda: get the wrapped return type of the invocable element i of tuple with given args
constexpr auto result_type = [](auto i) {
return wrap<std::invoke_result_t<decltype(std::get<i>(tup)), decltype(args)...>>;
};
// Recursive lambda call: get a tuple of wrapped return type using `result_type` lambda
constexpr auto valid_tuple = [=]() {
constexpr auto lambda = [=](auto&& self, auto i) {
if constexpr (i == size)
return std::make_tuple();
else if constexpr (is_valid(i))
return std::tuple_cat(std::make_tuple(result_type(i)), self(self, i + constant<1>));
else
return self(self, i + constant<1>);
};
return lambda(lambda, constant<std::size_t{0}>);
}();
// Lambda: get the underlying return types as wrapped variant
constexpr auto var_type =
std::apply([](auto... args) { return wrap<std::variant<unwrap<+args>...>>; }, valid_tuple);
/**
* Recursive lambda: get a variant of all underlying return type of matched functions, which
* contains the return value of calling function with given index and args.
*
* #param self The lambda itself
* #param tup A tuple of functions
* #param index The index to choose from matched (via args) functions
* #param i The running index to reach `index`
* #param j The in_place_index for constructing in variant
* #param args The variadic args for callling the function
* #return A variant of all underlying return types of matched functions
*/
constexpr auto lambda = [=](auto&& self, auto&& tup, std::size_t index, auto i, auto j,
auto&&... args) -> unwrap<+var_type> {
if constexpr (i == size)
throw std::invalid_argument("index too large");
else if (i == index) {
if constexpr (is_valid(i)) {
return unwrap<+var_type>{std::in_place_index<j>,
std::get<i>(tup)(decltype(args)(args)...)};
} else {
throw std::invalid_argument("invalid index");
}
} else {
return self(self, decltype(tup)(tup), index, i + constant<1>, j + constant<is_valid(i)>,
decltype(args)(args)...);
}
};
return lambda(lambda, std::forward<Tup>(tup), index, constant<std::size_t{0}>,
constant<std::size_t{0}>, decltype(args)(args)...);
};
}
In C++20, you can simplify this by
using std::remove_cvref_t<Tup> instead of std::remove_const_t<std::remove_reference_t<Tup>>
changing the definition of unwrap to:
template<auto A>
using unwrap = typename decltype(A)::type;
and using it as unwrap<...> instead of unwrap<+...>, which also allows removing the operator+ from wrap_t.
The purpose of wrap/unwrap:
wrap_t is meant to turn a type into a value that I can pass into functions and return from them without creating an object of the original type (which could cause all kinds of issues). It is really just an empty struct templated on the type and a type alias type which gives back the type.
I wrote wrap as a global inline variable, so that I can write wrap<int> instead of wrap<int>{}, since I consider the additional braces annoying.
unwrap<...> isn't really needed. typename decltype(...)::type does the same, it just gives back the type that an instance of wrap represents.
But again I wanted some easier way of writing it, but without C++20 this is not really possible in a nice way. In C++20 I can just pass the wrap object directly as template argument, but that doesn't work in C++17.
So in C++17 I "decay" the object to a pointer, which can be a non-type template argument, with an overloaded operator+, mimicking the syntax of the common lambda-to-function-pointer trick using the unary + operator (but I could have used any other unary operator).
The actual pointer value doesn't matter, I only need the type, but the template argument must be a constant expression, so I let it be a null pointer. The latter requirement is why I am not using the built-in address-of operator & instead of an overloaded +.
I want to allow multiple signatures for a callable whose type is specified as a template parameter. More specifically, I have a templated update method, which takes a callable that must return a float, and uses it to update values in a grid of data. A simplified illustration of this is
template <typename Fn>
void update(Fn&& fn_)
{
for (Vec3 pos : m_grid)
{
float val = fn_(pos, m_grid)
m_grid(pos) = val;
...
In the above case, the signature of the fn_ must always have both a pos and a grid as parameters, even if they are ignored in the implementation
I would like to use some template magic to allow multiple permutations of callback signatures.
In particular, I would like to allow callbacks that take pos but not grid, and callbacks that take no parameters at all. I don't mind whether the ordering of parameters is enforced or not.
Anyone any hints on how to do this? I don't mind using boost or other libraries, but they should be header-only.
You could do this with a helper function using SFINAE using is_invocable (C++17: std::is_invocable, or earlier with boost: boost::callable_traits::is_invocable)
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Vec3, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(pos_, grid_);
}
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Vec3>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(pos_);
}
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(grid_);
}
template <typename Fn>
void update(Fn&& fn_)
{
for (Vec3 pos : m_grid)
{
float val = call_helper(fn_, pos, m_grid)
m_grid(pos) = val;
...
In particular, I would like to allow callbacks that take pos but not grid, and callbacks that take no parameters at all.
Just define two overloads and use lambdas to do that by forwarding the request to the complete function and thus filtering the extra parameters.
As a minimal, working example:
struct S {
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(0, 0), void())
{
// ...
fn_(0, 0);
// ...
}
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(0), void())
{ update([&fn_](auto a, auto) { fn_(a); }); }
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(), void())
{ update([&fn_](auto, auto) { fn_(); }); }
};
int main() {
S s;
s.update([](auto, auto) {});
s.update([](auto) {});
s.update([]() {});
}
Consider the following snippet:
#include <type_traits>
#include <string>
template<typename T>
std::string stringify (const T& value)
{ return "dummy"; }
template<typename T>
class foo_class
{
public:
template<typename Converter = std::string(&)(const T&),
class = typename std::enable_if<std::is_convertible<
typename std::result_of<Converter(const T&)>::type,
std::string>
::value>::type>
foo_class(const T &value, const Converter &converter = stringify<T>) {}
};
int main(int,const char*[])
{
int X = 7;
foo_class<int> x(X);
foo_class<int> y(X, stringify<int>);
}
The first constructor compiles just fine, however the second one fails with the following error under Clang 3.6.2:
candidate template ignored: substitution failure [with Converter = std::__cxx11::basic_string<char> (const int &)]: function cannot return function type 'std::__cxx11::basic_string<char> (const int &)'
foo_class(const T &value, const Converter &converter = stringify<T>) {}
I dug down and found one way to fix it, by changing const Converter & to just Converter in the parameter list, although this might not be the desired behavior in some cases.
The error is caused by something in the std::enable_if<..> clause. I can remove it and the code compiles (and runs) just fine.
I am primarily interested in the question "why" - why does it work in the first case (when the function is selected as a default parameter), but not in the second case, when it is selected explicitly.
As a secondary question, what would be considered the best way to deal with the issue? I mean, I have one workaround, but would prefer to stick to the "const reference by default" policy for arguments that are not functions.
That's not how you are supposed to use result_of.
It should always be used with references, where the kind of the reference designates the value category of the corresponding expression in the call whose result you want to know.
So if you are calling converter as a const lvalue, then you'd do result_of<const Converter &(const T&)>. A few more examples:
// forward the value category of the function object
template<class F>
auto f(F&& f) -> std::result_of_t<F&&()> { return std::forward<F>(f)(); }
// always call as an lvalue, accept both lvalue and rvalue function object
template<class F>
auto f(F&& f) -> std::result_of_t<F&()> { return f(); }
// Accept everything by value, but call with all lvalues
template<class F, class... Args>
auto f(F f, Args... args) -> std::result_of_t<F&(Args&...)> { return f(args...); }
// Accept everything by value, and move them all
template<class F, class... Args>
auto f(F f, Args... args) -> std::result_of_t<F&&(Args&&...)> { return std::move(f)(std::move(args)...); }
In your second snippet, you have
Converter = std::string(const T&) // without reference
2 possibles fixes/workarounds:
Use std::decay_t
template<typename T>
class foo_class
{
public:
template<typename Converter = std::string(&)(const T&),
std::enable_if_t<
std::is_convertible<
std::result_of_t<std::decay_t<Converter>(const T&)>,
std::string>::value>* = nullptr>
foo_class(const T &value, const Converter &converter = stringify<T>) {}
};
Demo
or use
foo_class<int> y(X, &stringfy<int>);