Sign in to follow this  
Ectara

Template Parameter Deduction In Function Overloading

Recommended Posts

Ectara    3097

I've recently been perplexed by an error and it is halting my progress. Here is a minimal example showing the problem:
 

template <typename first, class second>
struct Example{
        enum Bits{
                Bit1 = 1,
                Bit2 = 2
        };
};

template <typename first, class second>
inline typename Example<first, second>::Bits foo(typename Example<first, second>::Bits lhs,
                                                                        typename Example<first, second>::Bits rhs)
{ return typename Example<first, second>::Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }

int main(){
        Example<int, int>::Bits b1 = Example<int, int>::Bit1;
        Example<int, int>::Bits b2 = Example<int, int>::Bit2;
        
        foo(b1, b2);
        
        
        return 0;
}


When I attempt to compile this, I get the error:

 

tests/file_test.cpp:20:19: error: no matching function for call to ‘foo(Example<int, int>::Bits&, Example<int, int>::Bits&)’
tests/file_test.cpp:20:19: note: candidate is:
tests/file_test.cpp:10:46: note: template<class first, class second> typename Example::Bits foo(typename Example<first, second>::Bits, typename Example<first, second>::Bits)
tests/file_test.cpp:10:46: note:   template argument deduction/substitution failed:
tests/file_test.cpp:20:19: note:   couldn't deduce template parameter ‘first’
 

Can anyone point out the issue in the above code? To me, the template parameter is obvious.

Share this post


Link to post
Share on other sites
Juliean    7067
template <typename first, class second>
inline typename Example<first, second>::Bits foo(typename Example<first, second>::Bits lhs,
typename Example<first, second>::Bits rhs)
{ return typename Example<first, second>::Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }

 

It appears your to me function definition is wrong, don't have a compiler at my hands right now, but shouldn't it go more along those lines?

 

template <typename first, class second>
inline Example Example<first, second>::Bits foo(typename Example<first, second>::Bits lhs,
                                                                        typename Example<first, second>::Bits rhs)
{ return Example<first, second>::Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }

From what I recall the typename keywords don't belong outside the template.

Edited by Juliean

Share this post


Link to post
Share on other sites
Ectara    3097



It appears your to me function definition is wrong, don't have a compiler at my hands right now, but shouldn't it go more along those lines?



template
inline Example Example::Bits foo(typename Example::Bits lhs,
typename Example::Bits rhs)
{ return Example::Bits(static_cast(lhs) | static_cast(rhs)); }

From what I recall the typename keywords don't belong outside the template.


The provided definition doesn't compile as-is; removing the "Example" after "inline", which was likely a typo, results in the compiler informing me that "typename" is needed before the return type, and having "typename" in the function body after "return" seems to make no difference whatsoever in error output. Leaving the extra "Example" in, out of curiosity, results in an error that foo() was not declared.
 

Edited by Ectara

Share this post


Link to post
Share on other sites
Juliean    7067

Ah, sorry, I confused what you really wanted to do here. Still you are going to need to provide 1. the class the function is inlined to and 2. the return type. Maybe this?

 

template <typename first, class second>
inline Example<first, second>::Bits Example<first, second>::Bits foo(typename Example<first, second>::Bits lhs,
typename Example<first, second>::Bits rhs)
{ return Example<first, second>::Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }
 

Share this post


Link to post
Share on other sites
Ectara    3097

Ah, sorry, I confused what you really wanted to do here. Still you are going to need to provide 1. the class the function is inlined to and 2. the return type. Maybe this?

 

template <typename first, class second>
inline Example<first, second>::Bits Example<first, second>::Bits foo(typename Example<first, second>::Bits lhs,
typename Example<first, second>::Bits rhs)
{ return Example<first, second>::Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }
 

That's what gave the "'foo' was not declared in this scope" error.

Share this post


Link to post
Share on other sites
Juliean    7067

That's what gave the "'foo' was not declared in this scope" error.

 

Well, of course it does. You are inlining the function into Example::Bits. This should work:

 

Example<int, int>::Bits.foo(b1, b2);

 

But I'm not sure this is exactly what you want, isn't it? It all seems overly complicated, maybe you could explain a little more what you really want to do?

Edited by Juliean

Share this post


Link to post
Share on other sites
Ectara    3097

Well, of course it does. You are inlining the function into Example::Bits. This should work:



Example::Bits.foo(b1, b2);

,>

"expected unqualified-id before ‘.’ token"

 

Unlike other languages like Java, an enum is not a class, and can't have member functions. "inline" just adds a suggestion to expand the function in-line where it is called, like an intelligent macro.

 

It all seems overly complicated, maybe you could explain a little more what you really want to do?

I have an enumeration of flags that is declared within a templated class, and I am defining an operator overload for bitwise operators so that they can be combined. It's essentially exactly as it looks at face value.

Share this post


Link to post
Share on other sites
BitMaster    8651
I'm a little pressed for time but I seem to remember that the compiler cannot deduce template parameters like that. Unless someone has a better idea, maybe try a template template function? I faintly recall solving a similar problem like that.

Share this post


Link to post
Share on other sites
Ectara    3097

I'm a little pressed for time but I seem to remember that the compiler cannot deduce template parameters like that. Unless someone has a better idea, maybe try a template template function? I faintly recall solving a similar problem like that.

That makes me wonder how that is so, because there are things like this, directly from GCC's basic_string.tcc:

  template<typename _CharT, typename _Traits, typename _Alloc>
    basic_istream<_CharT, _Traits>&
    operator>>(basic_istream<_CharT, _Traits>& __in,
	       basic_string<_CharT, _Traits, _Alloc>& __str)
    {
...
    }

The above, aside from not passing or returning any member type of a templated class, deduces the template parameters perfectly.

 

I can't quite picture how to make a templated template function for this purpose that doesn't unintentionally get called for other types.

Share this post


Link to post
Share on other sites
BitMaster    8651
In this case I have to pass. If you have clang lying around, maybe its error messages might be more helpful?

Edit: for the record, MSVC is saying the same with less information:
error C2783: 'Example<first,second>::Bits foo(const Example<first,second>::Bits &,const Example<first,second>::Bits &)' : could not deduce template argument for 'first'
see declaration of 'foo'
error C2783: 'Example<first,second>::Bits foo(const Example<first,second>::Bits &,const Example<first,second>::Bits &)' : could not deduce template argument for 'second'
see declaration of 'foo' Edited by BitMaster

Share this post


Link to post
Share on other sites
BitMaster    8651
Probably not what you want but MSVC accepts this at least:
template <typename first, typename second>
struct Example{
        enum Bits{
                Bit1 = 1,
                Bit2 = 2
        };

		Bits value;

		Example():value(0) { }
		Example(Bits bits):value(bits) { }

		Example& operator = (const Example& e) { value = e.value; return *this; } 
};

template <typename first, typename second>
inline Example<first, second> foo(
	typename Example<first, second> lhs,
    typename Example<first, second> rhs)
{
	return Example<first, second>(static_cast<Example<first, second>::Bits>(static_cast<unsigned int>(lhs.value) | static_cast<unsigned int>(rhs.value))); 
}

int main(){
        Example<int, int> b1 = Example<int, int>::Bit1;
        Example<int, int> b2 = Example<int, int>::Bit2;
        
        foo(b1, b2);
        
        
        return 0;
}

Share this post


Link to post
Share on other sites
Cornstalks    7030

What you're trying to do can't really be done. You can do something like this, though:

 

 
template <typename first, class second>
struct Example{
        enum Bits{
                Bit1 = 1,
                Bit2 = 2
        };
};
 
template <typename Bits>
inline Bits foo(Bits lhs, Bits rhs)
{ return Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }
 
int main(){
        Example<int, int>::Bits b1 = Example<int, int>::Bit1;
        Example<int, int>::Bits b2 = Example<int, int>::Bit2;
        
foo(b1, b2);
        
        return 0;
}

Share this post


Link to post
Share on other sites
Ectara    3097

What you're trying to do can't really be done. You can do something like this, though:

 

 

 
template <typename first, class second>
struct Example{
        enum Bits{
                Bit1 = 1,
                Bit2 = 2
        };
};
 
template <typename Bits>
inline Bits foo(Bits lhs, Bits rhs)
{ return Bits(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs)); }
 
int main(){
        Example<int, int>::Bits b1 = Example<int, int>::Bit1;
        Example<int, int>::Bits b2 = Example<int, int>::Bit2;
        
foo(b1, b2);
        
        return 0;
}

Yeah, I guess you're right. Since this is going to be an operator overload, I can't have it be that generic, so I'll have to move it out of the class, into the namespace above it, and see what I can do about resolving name collisions.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this