Template Parameter Deduction In Function Overloading

Started by
11 comments, last by Ectara 11 years ago

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.

Advertisement

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.



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.

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)); }
 

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.

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?

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.

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.

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.

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'

This topic is closed to new replies.

Advertisement