I can't clearly get the grasp of what it means when one mentions that a particular function, struct or ... is SFINAE-friendly.
Would someone please explain it?
When it allows substitution failure without hard error (as static_assert).
for example
template <typename T>
void call_f(const T& t)
{
t.f();
}
The function is declared for all T, even those which don't have f, so you cannot do SFINAE on call_f<WithoutF> as the method does exist. (Demo of non compiling code).
With following change:
template <typename T>
auto call_f(const T& t) ->decltype(t.f(), void())
{
t.f();
}
The method exists only for valid T.
so you can use SFINAE as
template<typename T>
auto call_f_if_available_impl(const T& t, int) -> decltype(call_f(t))
{
call_f(t);
}
template<typename T>
auto call_f_if_available_impl(const T& t, ...)
{
// Do nothing;
}
template<typename T>
auto call_f_if_available(const T& t)
{
call_f_if_available_impl(t, 0);
}
Note the int = 0 and ... is to order the overload.
Demo
--
An other case is when the template add special parameter to apply SFINAE for specialization:
template <typename T, typename Enabler = void> struct S;
And then
// Specialization only available for T which respect the traits.
template <typename T>
struct S<T, std::enable_if_t<my_type_trait<T>::value>>
{
};
An entity is termed SFINAE-friendly if it can be used in the context of SFINAE without producing a hard error upon substitution failure. I assume you already know what SFINAE is, as that is a whole other question in itself.
In the context of C++ standardization, the term SFINAE-friendly has so far been applied to std::result_of and std::common_type. Take the following example:
template <typename T>
void foo(T x, typename std::common_type<T, int>::type y) {}
void foo(std::string x, std::string y) {}
int main()
{
foo(std::string("hello"), std::string("world"));
}
Without SFINAE-friendly common_type, this would fail to compile, because std::common_type<std::string, int>::type would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly common_type (N3843) this example becomes well-formed, because std::common_type<std::string, int>::type produces a substitution failure so that overload is excluded from the viable set.
Here's a similar example with result_of:
template <typename T>
auto bar(T f) -> typename std::result_of<T()>::type { return f(); }
void bar(int n) {}
int main()
{
bar(42);
}
Without SFINAE-friendly result_of, this would fail to compile, because std::result_of<int()>::type would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly result_of (N3462) this example becomes well-formed, because std::result_of<int()>::type produces a substitution failure so that overload is excluded from the viable set.
Related
I have to provide an overload set of f, that accepts both member and member function pointers:
void g(int) {}
template <typename T, typename Field>
void f(const T& t, Field T::*field) { g(t.*field); }
template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const) { g((t.*getter)()); }
struct Foo {
int x = 0;
int y() const noexcept { return 1; }
};
int main() {
const Foo foo;
f(foo, &Foo::x);
f(foo, &Foo::y);
}
This works fine in C++11 and C++14, but breaks in C++17, because as of P0012R1, the noexcept specifier is part of the function type. To resolve this, an additional overload has to be added:
#if __cplusplus >= 201703L
template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const noexcept) { g((t.*getter)()); }
#endif
The macro guard are necessary, otherwise the code does not compile with older standards, such as C++11 or C++14 (the error is about redefinition of a function template).
As shown above, the implementation of both overloads are the same. Is it possible to provide a single overload that works in C++14 and C++17, without conditional compilation (#if/endif)? The goal is reduction of complexity, code duplication and testing burden.
Actual use case: https://github.com/Morgan-Stanley/binlog/pull/59/files#diff-043a057ac0b43822d0084562ace76697
Yes. Just write the one overload, total, and use std::invoke:
template <typename T, typename F>
void f(const T& t, F f) { g(std::invoke(f, t)); }
While std::invoke itself is C++17, it is implementable in C++11 - and it's probably just worth doing since it's generally useful. This approach not only handles noexcept member functions in C++17, but also reference-qualified and non-const-qualified member functions in C++11.
Although C++11 itself also contains an implementation of std::invoke - just in an unexpected place: std::reference_wrapper<T>:
template <typename T, typename F>
void f(const T& t, F f) { g(std::ref(f)(t)); }
In the C++ standard, N4618/[temp.deduct] (§14.8.2), the following example (§14.8.2/7) demonstrates how template parameter substitution is performed in lexical order:
template <class T> struct A { using X = typename T::X; };
template <class T> typename T::X f(typename A<T>::X);
template <class T> void f(...) { }
template <class T> auto g(typename A<T>::X) -> typename T::X;
template <class T> void g(...) { }
void h() {
f<int>(0);// OK, substituting return type causes deduction to fail
g<int>(0);// error, substituting parameter type instantiates A<int>
}
I was expecting requires-clauses content to be also evaluated before declaration content. I expected that the following modification would not generate any compilation error:
template <class T> struct A { using X = typename T::X; };
template <class T> typename T::X f(typename A<T>::X);
template <class T> void f(...) { }
template <class T>
requires false
auto g(typename A<T>::X) -> typename T::X;
template <class T>
requires true
void g(...) { }
void h() {
f<int>(0);// OK, substituting return type causes deduction to fail
g<int>(0);// error, substituting parameter type instantiates A<int>
}
Actually GCC tell me there is still an error. Is it the behaviour stated in the concept TS?
Yes. See the note in N4630 [temp.deduct]/5:
The satisfaction of constraints (14.10) associated with the function
template specialization is determined during overload resolution
(13.3), and not at the point of substitution.
Consider following code
template<typename T>
T modify(const T& item, std::function<T(const T&)> fn)
{
return fn(item);
}
When trying to use it as modify(5, [](const int& i){return 10*i;}); it fails to compile with
could not deduce template argument for 'std::function<T(const T &)> from lambda
I know that compiler can not deduce T from lambda, because lambda is not std::function, but isn't T already deduced from 5?
I can get over it using
template<typename T, typename F>
T modify(const T& item, const F& functor)
{
return functor(item);
}
for which previous example compiles, but it is in my opinion less intuitive. Is there a way to let the function argument to remain std::function and have it's template argument deduced automatically from item?
What you basically want to do is prevent deduction from happening. If template deduction occurs, it will fail (because a lambda is not a std::function<> - it doesn't matter that T was deduced from the first argument, deduction must succeed in every argument that is a deduced context). The way to prevent deduction is to stick the entire argument in a non-deduced context, the easiest way of doing that is to throw the type into a nested-name-specifier. We create such a type wrapper:
template <class T> struct non_deduce { using type = T; };
template <class T> using non_deduce_t = typename non_deduce<T>::type;
And then wrap the type in it:
template<typename T>
void foo(const T& item, std::function<void(T)> f);
template<typename T>
void bar(const T& item, non_deduce_t<std::function<void(T)>> f);
foo(4, [](int ){} ); // error
bar(4, [](int ){} ); // ok, we deduce T from item as int,
// which makes f of type std::function<void(int)>
Note, however, that:
template <typename T, typename F>
void quux(const T&, F );
is not really any less readable, and strictly more performant.
You can do it by using the identity trick as below:
template <typename T>
struct identity {
typedef T type;
};
template<typename T>
T modify(const T& item, typename identity<std::function<T(const T&)>>::type fn) {
return fn(item);
}
Live Demo
So, I'm following the example set by the code somewhere on this web page:
http://eli.thegreenplace.net/2014/sfinae-and-enable_if/
Here's what I have:
template<typename T>
void fun(const typename std::enable_if_t<std::is_integral<T>::value, T>& val) {
std::cout << "fun<int>";
}
template<typename T>
void fun(const typename std::enable_if_t<std::is_floating_point<T>::value, T>& val) {
std::cout << "fun<float>";
}
int main()
{
fun(4);
fun(4.4);
}
This way I would have to write:
fun<int>(4);
fun<double>(4.4);
How would I avoid that?
Compiler complains that it can't deduce the parameter T.
The examples are wrong, since T is in a non-deduced context. Unless you call the function like fun<int>(4);, the code won't compile, but this is probably not what the author intended to show.
The correct usage would be to allow T to be deduced by the compiler, and to place a SFINAE condition elsewhere, e.g., in a return type syntax:
template <typename T>
auto fun(const T& val)
-> typename std::enable_if<std::is_integral<T>::value>::type
{
std::cout << "fun<int>";
}
template <typename T>
auto fun(const T& val)
-> typename std::enable_if<std::is_floating_point<T>::value>::type
{
std::cout << "fun<float>";
}
DEMO
Also, the typenames in your code contradict your usage of std::enable_if_t.
Use either c++11:
typename std::enable_if<...>::type
or c++14:
std::enable_if_t<...>
How would that work in a constructor which doesn't have a return type though?
In case of constructors, the SFINAE condition can be hidden in a template parameter list:
struct A
{
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
A(const T& val)
{
std::cout << "A<int>";
}
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
A(const T& val)
{
std::cout << "A<float>";
}
};
DEMO 2
Alternatively, in c++20, you can use concepts for that:
A(const std::integral auto& val);
A(const std::floating_point auto& val);
To allow deduction you need a function parameter that is straightforwardly based on T. You then need to figure out where to put your enable_if (which indeed does not allow T to be deduced). Common options are on the return type or on an extra default parameter that you ignore.
Some good examples here: http://en.cppreference.com/w/cpp/types/enable_if
Is there a way to compare the result of decltype in C++11?
In other words, why is this code invalid:
template<typename T, typename U>
void func(T& t, U& u) {
if(decltype(t) == decltype(u)) {
// Some optimised version for this case
} else {
// A more general case for differing types
}
}
I know that in some cases this particular problem can be solved by partial template specialisation; my question is about comparison of decltypes.
Edit: The question came up in the course of trying to provide defaults for free functions through SFINAE. Perhaps a better question would have been why this is invalid:
template<bool B>
bool SomeFunction() { ... }
template<typename T, typename U>
bool SomeFunctionWrapper(T& t, U& u) {
SomeFunction<decltype(t) == decltype(u)>();
}
I've since found another solution (that doesn't involve templates at all) but at one stage I tried this:
// If it exists, the free function is defined as
// bool AFreeFunction();
typedef struct { char } undefined;
template<typename T = void>
undefined AFreeFunction();
template<bool B>
bool AFreeFunctionWrapper_() {
return false;
}
template<>
bool AFreeFunctionWrapper_<false>() {
return AFreeFunction();
}
bool AFreeFunctionWrapper() {
return AFreeFunctionWrapper_<decltype(AFreeFunction()) == decltype(undefined)>();
}
I eventually got a variant of this strategy working with GCC 4.6, but then discovered that default template arguments are not allowed for template functions in MSVC, even in the 2012 RC. So the eventual solution looks like this:
class AFreeFunction {
public:
operator bool() { return false; }
};
If the function is defined, it gets called. If it's not, it is instead interpreted as a constructor for the class, which is then implicitly cast to bool.
You normally solve this problem through tag dispatching. Also, you already have the "declared type" of t and u - T& and U&, respectively. To compare types for equality, you can use std::is_same. However, overload resolution already solves this problem for you:
template<class T>
void f(T& v1, T& v2){ ... } // #1
template<class T, class U>
void f(T& t, U& u){ ... } // #2
#1 is more specialized than #2 if both arguments to f are of the same type. If you, for whatever reason, insist on solving this through manual type comparision, here's how it would look applying the before mentioned points:
#include <type_traits>
namespace detail{
template<class T, class U>
void f(T& t, U& u, std::true_type){ ... } // #1
template<class T, class U>
void f(T& t, U& u, std::false_type){ ... } // #2
} // detail::
template<class T, class U>
void f(T& t, U& u){
detail::f(t, u, std::is_same<T,U>()); // tag dispatching
}
std::is_same will derive from std::true_type if both types are the same, and from std::false_type if not.
Why is it invalid? The result of a decltype is, well, a type. So it's saying something like
if (int == int)
which the language obviously doesn't allow.
You'll need to separate the two parts of the function and put the specialized part inside a function in a specialized class, and forward the call there. It's painful.
Or, you could use typeid or run-time type information, if your implementation implements it correctly, although that will defer everything to when the program runs (which allows for fewer optimizations).
You can do this using SFINAE (std::enable_if):
template<typename T, typename U>
typename std::enable_if<std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "same\n";
}
template<typename T, typename U>
typename std::enable_if<!std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "different\n";
}
As Mehrdad says, decltype(t) and decltype(u) are types (T & and U & respectively), not values, so cannot be compared at value-expression level but must be compared at meta-expression (template) level.