Search code examples
templatesc++11partial-specialization

Partial template specialization, invalid use of incomplete type


I have three example programs, two of which compile, and one of which does not. All three were compiled using g++ 4.8.1, using the command line: g++ --std=c++11 -Wall -pedantic -o foo foo.cc. The code in question is a throw-away example meant to exhibit the problem, and is not meant to be useful by itself.

The underlying problem is a templated class that requires a specialization to handle the fact that std::promise<void>::set_value() takes no arguments. This problem arises in the Foo::_call() method.

The version I want to work, call it version A, fails:

#include <future>

template <typename A, typename T>
class Foo
{
  private:
    std::function<T ()> _f;
    std::promise<T> _p;

    static void _call(std::function<T ()> &f, std::promise<T> &p) {
        p.set_value(f());
    }

  public:
    Foo(std::function<T ()> x) : _f(x) {}

    void set_promise() {
        _call(_f, _p);
    }
};

template<typename A>
inline void Foo<A, void>::_call(
    std::function<void ()> &f, std::promise<void> &p)
{
    f();
    p.set_value();
}

int bar()
{
    return 4;
}

void baz()
{
}

int main()
{
    Foo<int, int> a(bar);
    a.set_promise();
    Foo<int, void> b(baz);
    b.set_promise();
}

The error is:

foo.cc:24:53: error: invalid use of incomplete type ‘class Foo<A, void>’
     std::function<void ()> &f, std::promise<void> &p)
                                                     ^
foo.cc:4:7: error: declaration of ‘class Foo<A, void>’
 class Foo
       ^
foo.cc: In instantiation of ‘static void Foo<A, T>::_call(std::function<_Res()>&, std::promise<T>&) [with A = int; T = void]’:
foo.cc:18:21:   required from ‘void Foo<A, T>::set_promise() [with A = int; T = void]’
foo.cc:44:19:   required from here
foo.cc:11:9: error: invalid use of void expression
         p.set_value(f());
         ^


The following test, B, compiles. It takes out the extra template parameter A to Foo. In practice, I do not want this as my non-toy class needs that parameter.

#include <future>

template <typename T>
class Foo
{
  private:
    std::function<T ()> _f;
    std::promise<T> _p;

    static void _call(std::function<T ()> &f, std::promise<T> &p) {
        p.set_value(f());
    }

  public:
    Foo(std::function<T ()> x) : _f(x) {}

    void set_promise() {
        _call(_f, _p);
    }
};

template<>
void Foo<void>::_call(
    std::function<void ()> &f, std::promise<void> &p)
{
    f();
    p.set_value();
}

int bar()
{
    return 4;
}

void baz()
{
}

int main()
{
    Foo<int> a(bar);
    a.set_promise();
    Foo<void> b(baz);
    b.set_promise();
}


Test C also compiles. It pulls the _call() static method out of Foo to become a templated function. I would also rather not do this, as _call is meant to belong to Foo. Although this works, it is inelegant.

#include <future>

template<typename T>
inline void _call(std::function<T ()> &f, std::promise<T> &p)
{
    p.set_value(f());
}

template<>
void _call(std::function<void ()> &f, std::promise<void> &p)
{
    f();
    p.set_value();
}

template <typename A, typename T>
class Foo
{
  private:
    std::function<T ()> _f;
    std::promise<T> _p;

  public:
    Foo(std::function<T ()> x) : _f(x) {}

    void set_promise() {
        _call(_f, _p);
    }
};

int bar()
{
    return 4;
}

void baz()
{
}

int main()
{
    Foo<int, int> a(bar);
    a.set_promise();
    Foo<int, void> b(baz);
    b.set_promise();
}


So, the question is, why does A fail? Is there a way of solving this problem without resorting to B or C?


Solution

  • Partial template specialization only applies to the entire class template. You can't partially specialize just one member.

    One solution is to introduce a base class and specialize that instead.

    #include <future>
    
    template< typename T >
    struct Foo_base {
        static void _call(std::function<T ()> &f, std::promise<T> &p) {
            p.set_value(f());
        }
    };
    
    template<>
    struct Foo_base< void > {
        static void _call(std::function<void ()> &f, std::promise<void> &p)
        {
            f();
            p.set_value();
        }
    };
    
    template <typename A, typename T>
    class Foo : Foo_base< T >
    {
    private:
        using Foo::Foo_base::_call;
        std::function<T ()> _f;
        std::promise<T> _p;
    
    public:
        Foo(std::function<T ()> x) : _f(x) {}
    
        void set_promise() {
            _call(_f, _p);
        }
    };