Jump to content
  • Advertisement
Sign in to follow this  
Aressera

Template madness - template operator not found

This topic is 849 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am improving my template math library and I came upon the following problem:

typedef std:size_t Size;

template < typename T >
class A
{
	public:
	
		template < Size x >
		class B
		{
			T asdf[x];
		};
};

template < typename T, Size x >
inline A<T> operator + ( T value, const typename A<T>::template B<x>& vec )
{
	return A<T>();
}

int main( int argc, char** argv )
{
	A<Float>::B<2> b;
	5.0f + b; // ERROR: match for operator not found
}

For some reason the + operator is not found by the compiler. If i remove the first template argument from the operator (typename T) and explicitly use a type like float instead, the operator is found just fine.

 

Any idea how to work around this limitation? Note: B must be within the scope of A because both classes need the concrete type of the other.

Edited by Aressera

Share this post


Link to post
Share on other sites
Advertisement

Template madness indeed.

 

Why are you doing this? The few times I've seen people try to pull out fancy template-based math libraries they fail so badly in both performance and compilation times that they are quickly left by the roadside.  What is it you are hoping to accomplish here?

Share this post


Link to post
Share on other sites

With GCC 5.1, it's saying that it can't deduce the second parameter ("Size x") of the template.

 

Explicitly stating it works, however:

int main( int argc, char** argv )
{
	A<float>::B<2> b;
	//5.0f + b; // ERROR: match for operator not found
	
	operator+<float,2>(5.0f, b); //Compiles
}

Share this post


Link to post
Share on other sites

With VS2015, even operator+<float>(5.0f, b); works.

I have no idea why the unspecified version doesn't, since it seems like it ought to, but trying to reason about template deduction is best left to those on the fringe of sanity.

Share this post


Link to post
Share on other sites

I was trying to add swizzling to my vector classes and came upon this problem when implementing a full set of arithmetic operators for the swizzled vectors.

 

It's not a huge issue really, it only affects the operators where the first operand is a primitive type, the other operators can be class members and work fine.

Share this post


Link to post
Share on other sites
There is a very detailed, specific, complicated set of patterns that a C++ compiler is supposed to be able deduce as function template arguments.

One part of why this doesn't work is very simple: each parameter is deduced independently, so the float for the first argument doesn't help at all for the second one.

For the other part, I am not 100% sure, but generally there are strict restrictions regarding non-type template parameters and qualified name lookups, and you have both there. There is a simple example in the standard stating an argument type of the form A<T1>::B<T2> will not be deduced.

You might get closer to an acceptable pattern by taking B out of A.

Share this post


Link to post
Share on other sites

This works in VS2015:

# include <cstdint>
# include <iostream>
# include <vector>
# include <type_traits>
# include <string>


using namespace std;

template<typename T> class A {
	public:

		// internal B
		template<size_t x> class B {
			T asdf[x];
			};

		template<typename T> struct IsTypeB { 
			static const bool value = false;
			static const size_t size = 0;
			};

		template<size_t x> struct IsTypeB< B<x> > { 
			static const bool value = true;
			static const size_t size = x;
			};

		// internal C
		template<size_t x> class C {
			T asdf[x];
			};

		template<typename T> struct IsTypeC { 
			static const bool value = false;
			static const size_t size = 0;
			};

		template<size_t x> struct IsTypeC< C<x> > { 
			static const bool value = true;
			static const size_t size = x;
			};
	};

//template<typename T, Size x> inline A<T> operator+(T value, const typename A<T>::template B<x>& vec ) {
//	return A<T>();
//	}

template<typename T0, typename T1> 
	auto operator+(T0 v, const T1&  x) -> typename std::enable_if< A<T0>::IsTypeB<T1>::value , A<T0> >::type {
	cout << "size of B<x> = " << A<T0>::IsTypeB<T1>::size << endl;
	return A<T0>();
	}

template<typename T0, typename T1> 
	auto operator+(T0 v, const T1&  x) -> typename std::enable_if< A<T0>::IsTypeC<T1>::value , A<T0> >::type {
	cout << "size of C<x> = " << A<T0>::IsTypeC<T1>::size << endl;
	return A<T0>();
	}

	
// ----- main -----
void main() {

	A<float>::B<2> b;
	A<float>::C<3> c;

	5.0f + b;
	6.0f + c;

	cout << "done" << endl;

	getchar();
	}

As Pink Horror pointed out you're not gonna be able to deduce A<T1>::B<T2> directly. But you can 'hoist' it into the main class then use SFINAE to filter functions as necessary.

Share this post


Link to post
Share on other sites

You need 

template<class T>
template<Size x>
inline A<T> operator + ( T value, const typename A<T>::template B<x>& vec )
{
return A<T>();
}

Share this post


Link to post
Share on other sites

^ won't work since the operator is at namespace scope, gives a "too many template argument lists" error.

 

I managed to solve my issue by figuring out a way to put the class "B" at namespace scope (with a forward declaration). Now the operators are found as expected.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!