haelix
haelix

Reputation: 4555

Invalid use of incomplete type with templates and decltype

I need to make the following work.

This is a reduced version of my actual code, but basically the difficulty is the same, i.e., to deduce the return type of a factory method.

Specifically, I need either the second or third variant of DeduceObjectT (both commented), instead of the first, which requires the FactoryT::ObjectT typedef.

#include <string>
#include <utility>
#include <memory>

template<class FactoryT>
using DeduceObjectT = typename FactoryT::ObjectT;
//template<class FactoryT>
//using DeduceObjectT = typename decltype(std::declval<FactoryT>().create())::element_type;
//template<class FactoryT>
//using DeduceObjectT = typename std::result_of<decltype(&FactoryT::create)(FactoryT)>::type::element_type;

template<class FactoryT>
struct FactoryUser
{
    typedef DeduceObjectT<FactoryT> ObjectT;
};

template<class FactoryUserT>
struct Foo
{
    typedef typename FactoryUserT::ObjectT ObjectT;
};

struct StringFactory
{
    typedef std::string ObjectT; // want to omit this

    std::unique_ptr<std::string> create()
    {
        return nullptr;
    }

    Foo<FactoryUser<StringFactory>> t;
};

int main()
{
    StringFactory f;
    return 0;
}

After numerous tries I still get 'error: invalid use of incomplete type ‘struct StringFactory’'.

I also tried deducing the type by means of a default template argument of FactoryUser.

I really don't understand, why am I getting the error considering that the point that triggers instantiation of all templates is at the end -- the line that declares the data member t.

Compiler is gcc 4.7.3. with -std=c++0x -O0

Upvotes: 2

Views: 1589

Answers (2)

R Sahu
R Sahu

Reputation: 206657

You can alter Foo a little bit for your code to work:

#include <string>
#include <utility>
#include <memory>

template<class FactoryT>
using DeduceObjectT = typename FactoryT::ObjectT;

template<class FactoryT>
struct FactoryUser
{
    typedef DeduceObjectT<FactoryT> ObjectT;
};

// Provide a way for ObjectType to be specified at the time 
// the template is instantiated.
template<class FactoryUserT, typename ObjectType = typename FactoryUserT::ObjectT>
struct Foo
{
    typedef ObjectType ObjectT;
};

struct StringFactory
{
    std::unique_ptr<std::string> create()
    {
        return nullptr;
    }

    Foo<FactoryUser<StringFactory>, std::string> t;
};

int main()
{
   StringFactory f;
   return 0;
}

Upvotes: 1

erenon
erenon

Reputation: 19118

Try something like this instead:

template <typename Factory>
struct ProductTypedef
{
  typedef typename decltype(std::declval<Factory>().create())::element_type ObjectT;
};

struct StringFactory : public ProductTypedef<StringFactory> // CRTP
{
    std::unique_ptr<std::string> create()
    {
        return nullptr;
    }
};

Upvotes: 1

Related Questions