jkokorian
jkokorian

Reputation: 3095

Conditional member signature and implementation based on template type parameter

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

Answers (2)

ypnos
ypnos

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

Andriy Tylychko
Andriy Tylychko

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

Related Questions