Reputation: 3095
I am trying to write a template class with multiple type parameters T1
and T2
. The class has a private member of type std::promise<T2>
.
template <class T, class T2>
class Test
{
public:
void setValue(T2 value)
{
promise.set_value(value);
}
void setValue()
{
promise.set_value();
}
private:
std::promise<T2> promise;
};
This class compiles just fine when T2
is anything but void (as long as you don't call setValue
without parameters. When T2
is void, I get a compiler error:
error C2182: 'value' : illegal use of type 'void'
When T2
is anything but void, I would like to use the first setValue
method, which has a single parameter of type T2
. When T2
is void, I would like to use the second setValue
method, which takes no parameters. I've looked at a lot of examples, but I am relatively new to template programming, and I can't seem to make it work.
Is it possible to accomplish this with std::enable_if somehow? Or with template specialization?
Upvotes: 2
Views: 213
Reputation: 52417
You can solve this problem with a conditional dependency on a base class:
#include <future>
#include <type_traits>
#include <iostream>
template<class T2>
struct Base {
protected:
std::promise<T2> promise;
};
template<class T2>
struct BaseWithVariable : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<typename T2>
struct BaseWithoutVariable : public Base<T2> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test
: public std::conditional<std::is_same_v<T2, void>, BaseWithoutVariable<T2>, BaseWithVariable<T2>>::type
{
};
int main()
{
Test<int, int> a;
a.setValue(5);
Test<int, void> b;
b.setValue();
}
Now you realize that you can achieve the same with specialization on the level of the intermediate class:
template<class T2>
struct BaseSetter : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<>
struct BaseSetter<void> : public Base<void> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test : public BaseSetter<T2>
{
};
And it would also not hurt, in this particular case, to omit the use of Base
and just have both variants of BaseSetter
use their own member variable std::promise<T2>
, or std::promise<void>
, respectively.
However all of these crash in runtime with GCC 7.2.0. I don't know why.
Upvotes: 0
Reputation: 16286
helper template class specialisation:
#include <future>
template<typename T>
class TestHelper
{
public:
void setValue(T const& v)
{ promise.set_value(v); }
private:
std::promise<T> promise;
};
template<>
class TestHelper<void>
{
public:
void setValue()
{ promise.set_value(); }
private:
std::promise<void> promise;
};
template <class T, class T2>
class Test : public TestHelper<T2>
{
};
int main()
{
Test<void, int> t;
// t.setValue(); // compilation error: no matching function for call to 'std::promise<int>::set_value()'
t.setValue(0);
Test<void, void> t1;
t1.setValue();
// t1.setValue(0); // compilation error: no matching function for call to 'std::promise<void>::set_value(int)'
}
Upvotes: 2