Std::Enable_If to Conditionally Compile a Member Function

std::enable_if to conditionally compile a member function

SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. There is no such substitution.

I thought of that too and tried to use std::is_same< T, int >::value and ! std::is_same< T, int >::value which gives the same result.

That's because when the class template is instantiated (which happens when you create an object of type Y<int> among other cases), it instantiates all its member declarations (not necessarily their definitions/bodies!). Among them are also its member templates. Note that T is known then, and !std::is_same< T, int >::value yields false. So it will create a class Y<int> which contains

class Y<int> {
public:
/* instantiated from
template < typename = typename std::enable_if<
std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/

template < typename = typename std::enable_if< true >::type >
int foo();

/* instantiated from

template < typename = typename std::enable_if<
! std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/

template < typename = typename std::enable_if< false >::type >
int foo();
};

The std::enable_if<false>::type accesses a non-existing type, so that declaration is ill-formed. And thus your program is invalid.

You need to make the member templates' enable_if depend on a parameter of the member template itself. Then the declarations are valid, because the whole type is still dependent. When you try to call one of them, argument deduction for their template arguments happen and SFINAE happens as expected. See this question and the corresponding answer on how to do that.

Selecting a member function using different enable_if conditions

enable_if works because the substitution of a template argument resulted in an error, and so that substitution is dropped from the overload resolution set and only other viable overloads are considered by the compiler.

In your example, there is no substitution occurring when instantiating the member functions because the template argument T is already known at that time. The simplest way to achieve what you're attempting is to create a dummy template argument that is defaulted to T and use that to perform SFINAE.

template<typename T>
struct Point
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
std::cout << "T is int." << std::endl;
}

template<typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
std::cout << "T is not int." << std::endl;
}
};

Edit:

As HostileFork mentions in the comments, the original example leaves the possibility of the user explicitly specifying template arguments for the member functions and getting an incorrect result. The following should prevent explicit specializations of the member functions from compiling.

template<typename T>
struct Point
{
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is int." << std::endl;
}

template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is not int." << std::endl;
}
};

usage of enable_if for non-templated member function [duplicate]

first of all, as mentioned by @jrok and the post you linked to:
you have to have a template function to use enable_if. but it is not a good idea in your particular case, because there is no reason to do this operator-> templated. moreover, it wouldn't work! cuz there is no way to instantiate a particular operator->()! it doesn't have (and can't) any parameters and there is no syntax to specify it when you call it on some object! so, T (or whateve dummy type) will/could not be deduced from this call.

so, as a workaround you may use compile-time conditional inheritance. i.e. smth like this:

template <typename T, bool IsClass>
struct smart_base
{
struct base {};
};

template <typename T>
struct smart_base<T, true>
{
struct base
{
T* operator->()
{
// do that ever you wanted to do
}
};
};

template <typename T>
struct smart : public smart_base<T, std::is_class<T>::value>::base
{
// depending on T here you have, or have no operator-> inherited
};

you have to understand, that having operator-> actually in your base class will require to move some function and/or data members to base-of-the-base class :) or you may use CRTP technique to access members of a derived class from the base one :)

enable_if to conditionally include member functions

std::enable_if needs to depend on a parameter of the member template itself.

template <typename TagType>
class foo
{
public:
template <typename U = TagType>
typename std::enable_if<
std::is_base_of<std::bidirectional_iterator_tag,
U>::value,
foo>::type
operator --() {
return *this;
}
};

SFINAE will work as expected.

int main() {
foo<std::random_access_iterator_tag> f;
foo<std::forward_iterator_tag> f2;
--f; // fine
--f2;
}

main.cpp:24:3: error: no match for 'operator--' (operand type is 'foo<std::forward_iterator_tag>')

--f2;

C++ conditional template member function

If you can guarantee that all types appear only once, then the following should work:

template<typename... Ts>
class Executor {
using TupleOfCallback = std::tuple<std::function<void(Ts)>...>;
public:
Executor(const std::function<void(Ts)>&... func);

template<class E>
std::enable_if_t<(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity) {
std::get<std::function<void(E*)>>(m_Callbacks)(entity);
}

template<class E>
std::enable_if_t<!(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity)
{ }

public:
TupleOfCallback m_Callbacks;
};

The basic idea is to use fold-expression to detect whether E* is included in Ts..., thereby enabling the corresponding function.

Demo.

Using enable_if to prevent declaration? [duplicate]

You mustn't use this way because your object will be created in any case since the class doesn't have a condition to be created or not. And then here Enable_if<false, T> f0(int x) {};, the condition is false then there is no type. note that template class is instantiated on passing your parameter in the beginning and all that has done is replacing T by void.

one way you can use is as follows

#include <type_traits>
using namespace std;

template<bool B, typename T>
using Enable_if = typename std::enable_if<B,T>::type;


struct X {
template <class T>
Enable_if<true, T> f0(int x) {}
template <class T>
Enable_if<false, T> f0(int x) {}
};

int main(void)
{
X xx;
xx.f0<void>(4);
return 0;
}

Here we have two function templates, hence each one of them may be instantiated or not and this depends on the condition inside the class. If it's true, the function will be instantiated and vice versa. note that here only the needed function is instantiated.

conditional compilation of void argument member method using enable_if

Your first attempt with enable_if does not work because SFINAE applies in overload resolution
of function (or member function) templates, where it will eliminate
a specialization of the function template from the overload set when that
specialization cannot compile.

The member hello, in your first attempt, is not a member function template.
It has no template parameters. It is simply a member function of a class template.

Its return type is formulated by an enable_if expression that
will provoke compilation failure if the class template parameter T is not
instantiated as bool. This doesn't make the member function itself into a template.
SFINAE has no application. Once you declare MyClass<float> myclass2, the
specialization of MyClass<T> and all its members is completely determined.
The member function hello of that specialization must be instantiated,
and with T = float the attempt to do so must fail to compile.

In the second, successful attempt, hello is a member function template (of
a class template). It has a template parameter, Q, which by default is = T.
So SFINAE applies and you can leverage it with enable_if in the intended manner.
You may declare MyClass<float> myclass2 without error, because doing so
does not force any instantiation of the template member MyClass<float>::hello<Q>

Since you have written only one overload of hello, there is only one specialization
of the member function template for any choice of Q. When Q = bool, that
single specialization survives and myclass1.hello() will
compile. When Q != bool, SFINAE eliminates the that single specialization
and myclass2.hello() does not compile.

To appeciate vividly how SFINAE in the second case is operating at instantation of
the member function template, consider that:

  MyClass<float> myclass2;
myclass2.hello<bool>();

is fine; while on the other hand:

  MyClass<bool> myclass1;
myclass1.hello<float>();

does not compile.

Here is documentation of SFINAE

SFINAE not working to conditionally compile member function template

Try with

template<typename T>
struct C {
template<typename Q = T,
typename std::enable_if<std::is_same<Q, int>::value, bool>::type = true>
int foo() { // .............................................^^^^^^^^^^^^^^^^^^^^
return 1;
}

template<typename Q = T,
typename std::enable_if<!std::is_same<Q, int>::value, bool>::type = true>
int foo() { // ..............................................^^^^^^^^^^^^^^^^^^^^
return 0;
}

};

the point is that in your code SFINAE will enable/disable default values for template type parameter; but default values do not participate in overload resolution, so, in your case, you have two functions

template<typename, typename = void>
int foo() {
return 1;
}

template<typename, typename>
int foo() {
return 0;
}

with the same signature; so the compiler can't choose between the two and will give an error.

The code I proposed is different because in case the test of std::enable_if is false, you don't have the type (the element on the left of the =), not the value. Something as

// ................VVVVVV   what is = true ?
template<typename, = true>
int foo() {
return 1;
}

that is a true "substitution failure" that disable the method.



Related Topics



Leave a reply



Submit