C++ SFINAE enable_if_t in member function, how to disambiguate? - c++

Suppose we have some SFINAE member function:
class foo{
template <class S, class = std::enable_if_t<std::is_integral<S>::value, S>
void bar(S&& s);
template <class S, class = std::enable_if_t<!std::is_integral<S>::value, S>
void bar(S&& s);
}
If we declared it as above, then how can we define them? Both of their function signatures would look like:
template <class S, class>
inline void foo::bar(S&& s){ ... do something ... }
I have seen examples where one returns an std::enable_if_t<...> like:
template <class S, class>
auto bar(S&& s) -> std::enable_if_t<!std::is_integral<S>::value, S>(...){
... do something ...
}
To disambiguate based off of the return type. But I don't want to return anything.

since default arguments are not part of a function signature, make them not default
class foo{
template <class S, typename std::enable_if<std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
template <class S, typename std::enable_if<!std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
};
Live Demo
EDIT: by popular demand, Here's the same code in C++17:
class foo{
public:
template <class S>
void bar(S&& s)
{
if constexpr(std::is_integral_v<S>)
std::cout << "is integral\n";
else
std::cout << "NOT integral\n";
}
};
constexpr if statements are special to the compiler because the branch is chosen at compile time, and the non-taken branch isn't even instantiated
C++17 Demo

You can still do this in the return type just fine. Just keep the default of enable_if (which is void). Even if you're just on C++11, just add this alias:
template <bool B, typename T=void>
using enable_if_t = typename std::enable_if<B, T>::type;
And then you can do:
template <class S>
enable_if_t<std::is_integral<S>::value>
bar(S);
template <class S>
enable_if_t<!std::is_integral<S>::value>
bar(S);
Or:
template <class S>
auto bar(S) -> enable_if_t<std::is_integral<S>::value>
template <class S>
auto bar(S) -> enable_if_t<!std::is_integral<S>::value>
Either way, you have two properly disambiguated functions that return void.

With C++11 compiler another option is to use tag dispatching.
template <class S>
void bar(S&& s)
{
bar(std::forward<S>(s), std::is_integral<S>{});
}
template <class S>
void bar(S&& s, std::true_type)
{
...
}
template <class S>
void bar(S&& s, std::false_type)
{
...
}

Related

Check whether a default-deleted function template is explicitly specialized for a specific type?

(This question has been significantly edited, sorry.)
Suppose I have several non-constexpr function templates, which default to being deleted:
template <typename T> void foo() = delete;
template <typename T> int bar(int x) = delete;
// etc.
and have some explicit specialization as an exception to the general-case deletion.
I want to write code (e.g. a trait class?) which, given the identifier of one of these functions and a type T, detects, at compile-time, whether the specified function is explicitly specialized for type T. The code needs to be generic, i.e. not a separate detector for each of the functions.
Notes:
Looking for a C++11 solution.
We may assume the specified function is deleted by default - if that helps.
Ideally, it would like like instantiation_exists<decltype(foo), foo, int>::value or instantiation_exists<int>(foo, tag<int>) or instantiation_exists(foo, tag<int>) or something along those lines.
Edit: #Jarod42's wrote up an SFINAE example in a comment on an earlier version of this question, which was about a per-single-function detector. I tried to generalize/genericize it using a template-template parameter:
#include <type_traits>
template <typename T> void foo() = delete;
template <> void foo<int>() {}
template <template<typename U> typename F, typename T, typename = decltype(F<T>()) >
std::true_type test(int);
template <template<typename U> typename F, typename T>
std::false_type test(...);
template <typename T>
using foo_is_defined = decltype(test<foo<T>, T>(0));
static_assert(foo_is_defined<int>::value);
static_assert(not foo_is_defined<int*>::value);
but that was a wash (Coliru).
We cannot pass template function, or overloads in template parameter.
We can turn those function in functor:
template <typename T>
struct identity_type
{
using type = T;
};
template <typename F, typename T, typename = decltype(std::declval<F>()(identity_type<T>{})) >
std::true_type test(int);
template <typename F, typename T>
std::false_type test(...);
auto foos = [](auto tag, auto&&... args)
-> decltype(foo<typename decltype(tag)::type>((decltype(args))(args)...))
{
return foo<typename decltype(tag)::type>((decltype(args))(args)...);
};
template <typename T>
using is_foo = decltype(test<decltype(foos), T>(0));
Demo
I use generic lambda, so C++14.
in C++11, it would be really verbose:
struct foos
{
template <typename T, typename... Ts>
auto operator()(identity_type<T>, Ts&&... args) const
-> decltype(foo<T>(std::forward<Ts>(args)...))
{
return foo<T>(std::forward<Ts>(args)...);
};
};

C++ template function with std::optional [duplicate]

I'm trying to write syntactic sugar, in a monad-style, over std::optional. Please consider:
template<class T>
void f(std::optional<T>)
{}
As is, this function cannot be called with a non-optional T1 (e.g. an int), even though there exists a conversion from T to std::optional<T>2.
Is there a way to make f accept an std::optional<T> or a T (converted to an optional at the caller site), without defining an overload3?
1) f(0): error: no matching function for call to 'f(int)' and note: template argument deduction/substitution failed, (demo).
2) Because template argument deduction doesn't consider conversions.
3) Overloading is an acceptable solution for a unary function, but starts to be an annoyance when you have binary functions like operator+(optional, optional), and is a pain for ternary, 4-ary, etc. functions.
Another version. This one doesn't involve anything:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class template argument deduction already does the right thing here. If t is an optional, the copy deduction candidate will be preferred and we get the same type back. Otherwise, we wrap it.
Instead of taking optional as argument take deductible template parameter:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
Live example
This uses one of my favorite type traits, which can check any all-type template against a type to see if it's the template for it.
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
https://godbolt.org/z/HXgoEE
Another version. This one doesn't involve writing traits:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}

C++ template class, template member friend function matching rules

I have a templated class with a templated friend function declaration that is not having its signature matched when stated in a more direct, but seemingly equivalent, expression:
link to example on online compiler
#include <type_traits>
template <typename Sig> class Base;
template <typename R, typename ... Args> class Base<R(Args...)> { };
template <typename Sig, typename T> class Derived;
template <typename Sig> struct remove_membership;
template <typename T, typename R, typename ... Args>
class Derived<R(Args...), T> : public Base<R(Args...)> {
static void bar() { }
// XXX: why are these two not equivalent, and only the 1st version successful?
template <typename T2>
friend auto foo(T2 const &) -> Base<typename
remove_membership<decltype(&std::remove_reference_t<T2>::operator())>::type> *;
template <typename T2>
friend auto foo(T2 const &) -> Base<R(Args...)> *;
};
template <typename F, typename R, typename ... Args>
struct remove_membership<R (F::*)(Args...) const> {
using type = R(Args...);
};
template <typename T>
auto foo(T const &) -> Base<typename
remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type> *
{
using base_param_t = typename remove_membership<
decltype(&std::remove_reference_t<T>::operator())>::type;
Derived<base_param_t, T>::bar();
return nullptr;
}
int main(int, char **) { foo([](){}); } // XXX blows up if verbose friend decl. removed.
Inside member definitions of Derived<R(Args...), T> (for example, in the body of bar()), the types match, adding to my confusion:
static_assert(std::is_same<Base<R(Args...)>, Base<typename
remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type>>::value,
"signature mismatch");
Are there rules around template class template member function (and friend function) delarations and instantiations that make these preceding declarations distinct in some or all circumstances?
template <typename T2>
void foo(T2 const &)
template <typename T2>
auto foo(T2 const &)
-> std::enable_if_t<some_traits<T2>::value>;
Are 2 different overloads. Even if both return void (when valid).
2nd overload uses SFINAE.
(and yes, template functions can differ only by return type contrary to regular functions).
Your version is not identical but similar (&std::remove_reference_t<T>::operator() should be valid)
You can use the simpler template friend function:
template <typename T, typename R, typename ... Args>
class Derived<R(Args...), T> : public Base<R(Args...)> {
static void bar() { }
template <typename T2>
friend auto foo(T2 const &) -> Base<R(Args...)>*;
};
template <typename T>
auto foo(T const &) -> Base<void()>* // friend with Derived<void(), U>
{
using base_param_t = typename remove_membership<
decltype(&std::remove_reference_t<T>::operator())>::type;
Derived<base_param_t, T>::bar();
return nullptr;
}
Demo
but you have then to implement different version of the template foo.
The problem can be reduced to:
template<class T>
struct identity {
using type=T;
};
class X {
int bar();
public:
template<class T>
friend T foo();
};
template<class T>
typename identity<T>::type foo() { return X{}.bar(); }
int main() {
foo<int>(); // error: bar is a private member of X
}
Even though we know identity<T>::type is always T, the compiler doesn't know that and would be wrong to assume so. There could be a specialization of identity<T> somewhere later in the code that resolves to some type other than T.
Therefore when the compiler sees the second declaration of foo it won't assume that it is the same friend foo declared before.

Hide class template instance based on traits

I have a traits class like the following that reflects the compatibility between two types:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
The single member determines if SpecialMethod() can be called on objects of type ObjectType with argument of type ArgumentType.
A simple class that supports this is the following:
class ClassWithSpecialMethod
{
public:
template <typename T>
void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};
template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
static const bool SpecialMethodAvailable = true;
};
I want to write a worker class that uses this traits class and calls the special method if it is available. Basically something like the following:
template <typename T>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
//if Traits<GlobalDataType, T>::SpecialMethodAvailable
// call the method
//else
// do something different
}
};
I tried to realize this using std::enable_if. My solution works with the Visual C 14.1 compiler but not with GCC. Here is what I tried:
template <typename T, typename Enable = void>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
std::cout << "There is no special method (called with " << t << ")" << std::endl;
}
};
template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
};
I used this as follows:
typedef ... GlobalDataType; //before the template declarations
int main()
{
GlobalDataType td;
int integer = 0;
Worker<int>::DoSomething(integer, td);
}
If GlobalDataType is typedef'ed to ClassWithSpecialMethod, both VS and GCC compile fine and output correctly:
Special Method called with 0
However, if GlobalDataType is typedef'ed to something that does not allow the special method (e.g. int), VS still produces the correct output while GCC results in a compile error:
In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’:
source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type
GlobalDataType {aka int}’
Can someone explain why this does not work as intended under GCC? What would be alternatives?
Link to online compiler
As explained by Jarod42, this method
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
with GlobalDataType fixed as int, is ever wrong (for ever T type) because it's sure that int is without SpecialMethod().
To solve this problem with a minimum code change, you can templatize the second parameter
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
If you want that DoSomething() receive (as second parameter) only a GlobalDataType, you can impose it enabling DoSomething using SFINAE, only if U is GlobalDataType. Something as
template <typename U>
static typename std::enable_if<std::is_same<U, GlobalDataType>{}>
DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
What would be alternatives?
I propose you a completely different way, based (following the std::declval() example) over declaration of functions.
First of all, a couple of template helper functions
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethodHelper (int)
-> decltype(std::declval<ObjectType>.SpecialMethod(std::declval<Args>...),
std::true_type{} );
template <typename ... Args>
constexpr std::false_type withSpecialMethodHelper (long);
Now you can write the declaration of a template function that return std::true_type if ObjectType has a SpecialMethod() that can be called with a variadic list of arguments of type Args...
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
-> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
or maybe better, as suggested by Jarod42, through using
template <typename ObjectType, typename ... Args>
using withSpecialMethod
= decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
If you can use C++14, you can also define a withSpecialMethod_v template constexpr variable
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= decltype(withSpecialMethod<ObjectType, Args...>())::value;
in case of declared function or
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= withSpecialMethod<ObjectType, Args...>::value;
in case of using, that can simplify the use.
Now the Worker class and specialization become
template <typename T, bool = withSpecialMethod_v<GlobalDataType, T>>
struct Worker
{
static void DoSomething (T t, GlobalDataType & globalData)
{
std::cout << "There is no special method (called with " << t << ")"
<< std::endl;
}
};
template <typename T>
struct Worker<T, true>
{
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
};
Mvsc 14 doesn't do the 2 phases look-up needed for template.
gcc does (and is correct).
globalData.SpecialMethod(t); is incorrect for any t immediatly so the error. (globalData.SpecialMethod is incorrect and doesn't depend of template parameter).
By post-pone the evaluation you might have what you want:
template <typename T>
struct Worker<T, std::enable_if_t<Traits<GlobalDataType, T>::SpecialMethodAvailable>>
{
template <typename G, typename U>
static void f(G& g, U& u)
{
g.SpecialMethod(u);
}
static void DoSomething(T t, GlobalDataType& globalData)
{
f(globalData, t);
}
};
Demo

enable_if seems to work outside a class but not inside

Here is my somewhat odd code:
template <typename T&>
class A {
public:
void b(typename std::enable_if<!std::is_pointer<T>::value, T>;::type o) {}
void b(typename std::enable_if<std::is_pointer<T>::value, T>;::type o) {}
};
template <typename T>
void b(typename std::enable_if<!std::is_pointer<T>::value, T>::type o) {}
template <typename T>
void b(typename std::enable_if<std::is_pointer<T>::value, T>::type o) {}
If I ifdef out the method b and call b<int *>(pi) where pi is int *, everything compiles.
If I ifdef out the function b (outside class) and call A<int *> a; a.b(pi), I get the following error:
error: no type named 'type' in 'std::__1::enable_if<false, int *>'
Why the inconsistency and how can I fix the problem so that I can use the methods in A?
The problem is, that SFINAE only works during overload resolution and only if the function itself is a template. In your method case, the whole class is a template, meaning that there is no substitution of the template parameter (remember: SFINAE == "Substitution Failure Is Not An Error").
At the point of instantiation, the method signatures look like this (nevermind the call to them):
void A<int*>::b(std::enable_if<false, int*>::type o) // error
void A<int*>::b(std::enable_if<true, int*>::type o)
To fix this, make the methods templates too:
template<class T>
class A{
public:
template<class U>
void b(U o, typename std::enable_if<!std::is_pointer<U>::value>::type* = 0){}
// same for the other version
};
On a side note, letting the template argument get deduced is the better way to use SFINAE, so you should modify the free functions to look like this:
template<class T>
void b(T o, typename std::enable_if<!std::is_pointer<T>::value>::type* = 0){}
// same for the other version
In C++11, you can even use the template parameters for SFINAE:
template<class T, EnableIf<std::is_pointer<T>> = {}>
void b(T o);
Utilizing an alias from the blog entry linked from here:
namespace detail{ enum class enabler{}; }
template<class Cond, class T = detail::enabler>
using EnableIf = typename std::enable_if<C::value, T>::type;
For the explanation, see Xeo's answer.
For the work-around: just add a dummy template parameter to the method
#include <utility>
#include <type_traits>
template <typename T>
class A {
public:
template <typename U = T>
void b(typename std::enable_if<!std::is_pointer<U>::value, U>::type o);
template <typename U = T>
void b(typename std::enable_if<std::is_pointer<U>::value, U>::type o);
};
template <typename T>
template <typename U>
void A<T>::b(typename std::enable_if<!std::is_pointer<U>::value, U>::type o) {}
template <typename T>
template <typename U>
void A<T>::b(typename std::enable_if<std::is_pointer<U>::value, U>::type o) {}
int main() {
A<int> a;
a.b(0);
}
Live Demo Here.
You are not using SFINAE correctly because the compiler can't deduce the argument for enable_if<...>::type and probably that's why it fails.
Correct declarations of free-standing functions would be:
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, void>::type b(T o);
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, void>::type b(T o);
In this particular case plain function overloading can be used as well:
template <typename T>
void b(T);
template <typename T>
void b(T*);