Two different implementations of the same template constructor using SFINAE [duplicate] - c++11

Why does this code work:
template<
typename T,
std::enable_if_t<std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}
template<
typename T,
std::enable_if_t<!std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}
and can correctly distinguish between these two calls:
Add(1);
Add(1.0);
while the following code if compiled results in the redefinition of Add() error?
template<
typename T,
typename = typename std::enable_if<std::is_same<T, int>::value, T>::type>
void Add(T) {}
template<
typename T,
typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type>
void Add(T) {}
So if the template parameter is type, then we have redefinition of the function, if it is non-type, then everything is ok.

SFINAE is about substitution. So let us substitute!
template<
typename T,
std::enable_if_t<std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}
template<
typename T,
std::enable_if_t<!std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}
Becomes:
template<
class T=int,
int* = nullptr>
void Add(int) {}
template<
class T=int,
Substitution failure* = nullptr>
void Add(int) {
template<
class T=double,
Substitution failure* = nullptr>
void Add(double) {}
template<
class T=double
double* = nullptr>
void Add(double) {}
Remove failures we get:
template<
class T=int,
int* = nullptr>
void Add(int) {}
template<
class T=double
double* = nullptr>
void Add(double) {}
Now remove template parameter values:
template<
class T,
int*>
void Add(T) {}
template<
class T
double*>
void Add(T) {}
These are different templates.
Now the one that messes up:
template<
typename T,
typename = typename std::enable_if<std::is_same<T, int>::value, T>::type>
void Add(T) {}
template<
typename T,
typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type>
void Add(T) {}
Becomes:
template<
typename T=int,
typename =int>
void Add(int) {}
template<
typename int,
typename = Substitution failure >
void Add(int) {}
template<
typename T=double,
typename = Substitution failure >
void Add(double) {}
template<
typename T=double,
typename = double>
void Add(double) {}
Remove failures:
template<
typename T=int,
typename =int>
void Add(int) {}
template<
typename T=double,
typename = double>
void Add(double) {}
And now template parameter values:
template<
typename T,
typename>
void Add(T) {}
template<
typename T,
typename>
void Add(T) {}
These are the same template signature. And that is not allowed, error generated.
Why is there such a rule? Beyond the scope of this answer. I'm simply demonstrating how the two cases are different, and asserting that the standard treats them differently.
When you use a non-type template parameter like the above, you change the template signature not just the template parameter values. When you use a type template parameter like the above, you only change the template parameter values.

Here, the problem is that the template signature for add() is the same: A function template taking two parameters type.
So when you write:
template<
typename T,
typename = std::enable_if_t<std::is_same<T, int>::value, T>>
void Add(T) {}
That's fine, but when you write:
template<
typename T,
typename = std::enable_if_t<!std::is_same<T, int>::value, T>>
void Add(T) {}
You are redefining the first add() template, only this time you specify a different default type for the second template parameter: in the end, you defined an overload for add() with the exact same signature, hence the error.
If you want to have several implementations like your question suggests, you should use std::enable_if_t as the return parameter of your template or use it in the same fashion as your first example. So your initial code becomes:
template<typename T>
std::enable_if_t<std::is_same<T, int>::value> Add(T) {}
template<typename T>
std::enable_if_t<!std::is_same<T, int>::value> Add(T) {}
Working example on Coliru
On the code above, if T == int, the second signature becomes invalid and that triggers SFINAE.
NB: Suppose you want N implementations. You can use the same trick as above but you'll need to make sure only one boolean among the N is true and the N-1 that remain are false, otherwise you'll get the exact same error!

I think the problem is the fact that you can use a function even if a default template parameter does not compile by specifying a different value for it. Think of what would happen if you specified two template parameters in a call to add.

I'll try to first give an example without the use of templates, but with default arguments. The following example is comparable to why the second example of yours fails, although it is not indicative of the inner workings of template overload resolution.
You have two functions declared as such:
void foo(int _arg1, int _arg2 = 3);
And
void foo(int _arg1, int _arg2 = 4);
Hopefully you realize that this will fail to compile, their is never going to be a way to distinguish between the two calls to foo with the default argument. It's completely ambiguous, how will the compiler ever know which default to choose? You may wonder why i used this example, after all shouldn't the template in the first example deduce different types? The short answer to that is no, and that's because the "signature" of the two templates in your second example:
template<
typename T,
typename = typename std::enable_if<std::is_same<T, int>::value, T>::type>
void Add(T) {}
template<
typename T,
typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type>
void Add(T) {}
...have the exact same signature, which is:
template<typename T,typename>
void Add(T);
And (respectively)
template <typename T, typename>
void Add(T);
Now you should start to understand the similarity's between the example i gave with non-templates and the example you provided that failed.

SFINAE does not spread to default values for either types nor values. Only types of function arguments and result are used in this technique.

Related

How should I use unique pointer in c++11 when the destructor is private?

I'm managing the class whose destructor is private since the object must be assigned into heap.
Let's suppose this class as A.
std::unique_ptr<A> a(new A());
When this a is out of scope, destructor is called.
However, the default behavior of unique_ptr is calling "public destructor".
In this case, how should I do without making the destructor public?
You may define a deleter functional and make it friend with your class.
#include <memory>
class A{
friend struct D;
private:
~A() {}
};
struct D {
void operator()(A* a) const {
delete a;
}
};
int main(){
std::unique_ptr<A, D> a(new A());
return 0;
}
demo

How use std::iterator_traits for my custom container

I am implementing deque having the following structure:
template<typename T,
template<typename...> class Container = std::vector>
class Deque {
public:
void push_front(const T& obj);
void emplace_front(T&& obj);
void push_back(const T& obj);
void emplace_back(T&& obj);
auto pop_front();
auto pop_back();
std::size_t size() const;
bool empty() const;
};
I want to support apis like begin(), end(), cbegin() and cend() and they should return stl compliant iterators. I have heard that using std::iterator_traits it can be done. Help pointers in this regard is appreciated.
Also I want the iterators to be of type bidirectional.

Variadic templates and perfect forwarding to specific template class

I want to implement a perfect forwarding constructor in my "myArgs class",
which should only get involved for specializations of myClassBase<>.
Roughly speaking: call this constructor for each variation of myClassBase<>
But it won't compile: '<function-style-cast>' : cannot convert from 'myClass' to 'myArgs'.
I think it's because the compiler can't deduce from Args&& to myClassBase<T, D>.
See this (very basic) example:
template <class T, class D>
class myClassBase
{
private:
T data;
D data2;
};
typedef myClassBase<char, int> myClass;
class myArgs
{
public:
myClass m_data;
template <class T, class D>
myArgs(myClassBase<T, D>&& rhs) :
m_data(std::forward< myClassBase<T, D> >(rhs))
{
}
};
template <class... Args>
void var_args(Args&&... args)
{
myArgs( std::forward<Args>(args)... );
}
Test the stuff:
myClass x;
var_args(x);
If i change the var_args function parameter from void var_args(Args&&... args) to void var_args(Args... args) it works.
Btw. in my real code myClass of course supports move semantics.
Thanks in advance.
Christian.
template <class T, class D>
myArgs(myClassBase<T, D>&& rhs) :
m_data(std::forward< myClassBase<T, D> >(rhs))
{
}
rhs there is an rvalue reference, not a forwarding reference. A forwarding reference in a function parameter be of the form T&& where T is some deduced template parameter. You can fix this by having T deduced rather than specifying myClassBase.
template <class T>
myArgs(T&& rhs) :
m_data(std::forward<T>(rhs))
{
}
If you want this function to only be valid if T is a myClassBase, you can write a trait to check it:
template <typename T>
struct isMyClassBaseImpl : std::false_type{};
template <typename T, typename D>
struct isMyClassBaseImpl<myClassBase<T,D>> : std::true_type{};
template <typename T>
using isMyClassBase = isMyClassBaseImpl<std::decay_t<T>>;
Then you can SFINAE it out:
template <class T, std::enable_if_t<isMyClassBase<T>::value>* = nullptr>
myArgs(T&& rhs) :
m_data(std::forward<T>(rhs))
{
}

C++/CLI generics compilation error

I have problem with compilation such files.
CardSet.h
#include "Card.h"
#pragma once
using namespace System;
using namespace Collections;
generic <typename C>
public ref class CardSet<C>
{
public:
CardSet<C>();
};
CardSet.cpp
#include "CardSet.h"
generic <typename C>
CardSet<C>::CardSet()
{
}
I am following http://www.functionx.com/cppcli/classes2/Lesson24d.htm
Could sb advice what I am doing wrong?
Well, follow the syntax that the tutorial demonstrates, you don't repeat <C> everywhere. Correct syntax is:
generic <typename C>
public ref class CardSet {
public:
CardSet();
};
generic <typename C>
CardSet<C>::CardSet(){
}

Overloading with rvalue references

Perfect forwarding is great and all. But what do I do if I want to not perfectly forward and instead just overload, on something that happens to be a templated type?
template<typename T> void foo(T&& ref);
template<typename T> void foo(const T& ref);
Won't work because the first overload will invoke perfect forwarding. I'd really like behaviour very similar to if the first was a normal lvalue reference- where T will always be a value type.
#include <type_traits>
template<typename T>
typename std::enable_if
<
!std::is_reference<T>::value,
void
>::type
foo(T&& ref) {}
template<typename T> void foo(const T& ref) {}

Resources