Reputation: 1063
I have two different versions of my constructor (one for gpu's and one for cpu's), but the rest of the class is nearly the same for both versions.
I want to enable the cpu-variant of this class by an SFINAE enable_if statement.
The problem is, that I need to compile it without C++11 and with clang++.
The code below is working, but it generates me five warnings:
warning: default template arguments for a function template are a C++11
extension [-Wc++11-extensions]
Is there a way, to use sfinae without the need to compile the c++ code with standard C++11 and without the c++11 warnings?
Here is the code:
#include <iostream>
enum arch_t{cpu, gpu};
template<bool B, class T=void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
template<typename T1, arch_t arch>
class data {
public:
template<arch_t a=arch, typename enable = typename enable_if<a==cpu, void>::type, typename tmp = enable>
data(void)
{
std::cout << "-CPU()" << std::endl;
}
template<arch_t a=arch, typename = typename enable_if<a!=cpu, void>::type>
data(void)
{
std::cout << "-GPU()" << std::endl;
}
};
int main()
{
data<int,gpu> gpu_data;
data<int,cpu> cpu_data;
}
Expected (and also real) Output: -GPU() -CPU()
I use clang++-3.8 for compilation.
Upvotes: 2
Views: 341
Reputation: 66200
Non exactly what you asked (and a little silly solution, I suppose) but... if you can accept that your constructor receive a mandatory dummy argument (of undefined type), you can activate SFINAE on a second optional parameter.
The following is a working example
#include <iostream>
enum arch_t{cpu, gpu};
template <bool B, typename T = void>
struct enable_if
{ };
template <typename T>
struct enable_if<true, T>
{ typedef T type; };
template <typename T1, arch_t arch>
class data
{
public:
template <typename T>
data (T const &, typename enable_if<arch==cpu, T>::type * = 0)
{ std::cout << "-CPU()" << std::endl; }
template <typename T>
data (T const &, typename enable_if<arch!=cpu, T>::type * = 0)
{ std::cout << "-GPU()" << std::endl; }
};
int main ()
{
data<int,gpu> gpu_data(0);
data<int,cpu> cpu_data("abc");
}
Upvotes: 1
Reputation: 275385
enum arch_t{cpu, gpu};
template<arch_t, arch_t>
struct arch_tag { };
template<arch_t arch>
class data {
private:
public:
data(arch_tag<cpu, arch>) {
std::cout << "-CPU()" << std::endl;
}
data(arch_tag<gpu, arch>) {
std::cout << "-GPU()" << std::endl;
}
};
struct data_tag:arch_tag<cpu, cpu>, arch_tag<gpu, gpu> {};
int main(){
data<cpu> c = data_tag();
(void)c;
data<gpu> g = data_tag();
(void)g;
}
You do have to initialize data<gpu>
with data_tag
, but only the appropriate one is ever called.
Upvotes: 0
Reputation: 180500
Proposing this as an alternative solution.
Instead of using SFINAE we can use tags instead. Here, a tag is a empty struct whose name acts as a piece of information(a tag). We can then overload functions(this includes constructors) using the tag since it is a type. So in this case we could have something like
struct cpu_arch_t {} cpu_arch;
struct gpu_arch_t {} gpu_arch;
template<typename T1>
class data {
public:
data(cpu_arch_t)
{
std::cout << "-CPU()" << std::endl;
}
data(gpu_arch_t)
{
std::cout << "-GPU()" << std::endl;
}
};
And then we can use it like
int main()
{
data<int> gpu_data(gpu_arch);
data<int> cpu_data(cpu_arch);
}
Upvotes: 1