majakthecoder
majakthecoder

Reputation: 177

could not deduce template argument if using typedef

I have a problem with "Could not deduce template argument for T" error.

here is my class:

class PropertyAccess
{
public:
    template <typename TClass, typename TType>
    struct SetterPointer { typedef void (TClass::*Type)(const TType&); };

    template <typename TClass, typename TType>
    struct GetterPointer { typedef TType(TClass::*Type)(); };

    template <typename TClass, typename TType>
    void Assign(
        TClass* object,
        typename SetterPointer<TClass, TType>::Type setter,
        typename GetterPointer<TClass, TType>::Type getter)
    {
        ...
    }
}

and here is how I use it:

class Test
{
public:
    int a;

    void Set(const int& v) { a = v; }
    int Get() { return a; }
};

void main
{
    Test test;
    PropertyAccess property;
    property.Assign(&test, &Test::Set, &Test::Get);  <---here is compile error
}

If i try to compile that code, I have error:

'void PropertyAccess::Assign(TClass *,PropertyAccess::SetterPointer<TClass,TType>::Type,PropertyAccess::GetterPointer<TClass,TType>::Type)' : could not deduce template argument for 'TType'

but if I change Assign method to:

void Assign(
        TClass* object,
        typename SetterPointer<TClass, TType>::Type setter,
        TType(TClass::*getter)())
    {
        ...
    }

then everything is alright. Why? I know that function type cannot be deduced if it has no args, but why it works in second case?

I use Visual Studio 2013 C++ compiller.

Upvotes: 0

Views: 385

Answers (1)

AnT stands with Russia
AnT stands with Russia

Reputation: 320481

C++ cannot deduce the enclosing type from its nested type. It is an example of non-deduced context.

A simplest example would be something like

struct S { typedef int T; };

template <typename C> void foo(typename C::T i) {}

int main()  {
   int x = 0;
   foo(x); // ERROR: non-deduced context
}

or, closer to what you have in your code

template <typename X> struct S { typedef X T; };

template <typename X> void foo(typename S<X>::T i) {}

int main()  {
   int x = 0;
   foo(x); // ERROR: non-deduced context
}

The same thing happens in your case as well, in a slightly more convoluted form. For template method Assign it is not possible to deduce template parameters TClass and TType through a function parameter like typename SetterPointer<TClass, TType>::Type setter. In your case template parameter TClass is deducible from the first argument of Assign, but TType is not deducible from any argument. Hence the error.

When you change declaration of the last parametr of Assign to TType(TClass::*getter)() you immediately make the context deducible, i.e. the compiler uses the getter argument as an opportunity to deduce TType.

In C++11 you can use alias templates to achieve the same typedef effect and still keep the context deducible

class PropertyAccess
{
public:
    template <typename TClass, typename TType>
    using SetterPointer = void (TClass::*)(const TType&);

    template <typename TClass, typename TType>
    using GetterPointer = TType(TClass::*)();

    template <typename TClass, typename TType>
    void Assign(
        TClass* object,
        SetterPointer<TClass, TType> setter,
        GetterPointer<TClass, TType> getter)
    {
       ... 
    }
};

Upvotes: 4

Related Questions