Reputation: 321
the articles https://www.fluentcpp.com/2018/05/15/make-sfinae-pretty-1-what-value-sfinae-brings-to-code/ and https://www.fluentcpp.com/2018/05/18/make-sfinae-pretty-2-hidden-beauty-sfinae/ explain how to use SFINAE for class methods. The first implementation of the class is the following:
template<typename T>
class MyClass
{
public:
void f(T const& x);
void f(T&& x);
};
To prevent the use of the second function f
if T
is a reference, the article presents the following solution:
template<typename T>
class MyClass
{
public:
void f(T const& x){}
template<typename T_ = T, typename = std::enable_if_t<!std::is_reference_v<T_>>>
void f(T&& x){}
};
Wouldn't it be easier/cleaner to substitute the return type of the method by std::enable_if
? So the class would look like the following:
template<typename T>
class MyClass
{
public:
void f(T const& x){}
std::enable_if_t<!std::is_reference_v<T>>> f(T&& x){}
};
Upvotes: 3
Views: 1648
Reputation: 66200
Wouldn't it be easier/cleaner to substitute the return type of the method by enable_if?
I suppose is question of personal taste.
Anyway, take in count that your example
std::enable_if_t<!std::is_reference_v<T>>> f(T&& x){}
doesn't work because SFINAE works over templates of the specific method. So, also for your example, you have to use a T_
(let me call it U
) template type and the condition std::is_reference
must evaluate U
, not T
template <typename U = T>
std::enable_if_t<!std::is_reference_v<U>> f(T&& x){}
Anyway, for both solution there is the "hijacking" risk: you can circumvent the test explicating the U
type
myClassObject.template f<void>(aReference);
To avoid the hijacking risk, you have to impose also that T
and U
are the same type
template<typename U = T,
typename = std::enable_if_t<
!std::is_reference_v<U>
&& std::is_same_v<U, T>>>
void f(T&& x){}
template <typename U = T>
std::enable_if_t<
!std::is_reference_v<U>
&& std::is_same_v<U, T>> f(T&& x)
{ }
Upvotes: 1
Reputation: 275385
First, your technique doesn't work. T
is a fixed type within MyClass
enable_if_t<false>
is ill-formed. No substitution is being performed, so this isn't substitution failure, it is just an ill-formed signature.
You can fix it to some extent with
template<class T_=T>
std::enable_if_t<!std::is_reference_v<T_>>> f(T_&& x){}
Now, this technique (using the return value) doesn't work (A) on constructors, and (B) when you want to deduce the return type.
It also mixes the return type with SFINAE code; the two have nothing to do with each other.
On top of all of this, both this technique and the original one you are copying has some questionable interaction with the "all templates must have a valid instantiation". I am still unclear if this applies to every template method of a fixed template class instantiation or not; if so, then you have to ensure that some T_
would make the body valid.
Upvotes: 2