Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template Function Selection Based on Nested Type

The following code works correctly on VS2015:

struct Foo
{
   using Bar = int;
   auto operator()() { return "Foo!";  }
};

template <typename Callable, typename CodeType> // <<< CodeType is a template param
void funky(CodeType code, Callable func)
{
   cout << "Generic: " << code << ", " << func() << endl;
}

template <typename HasBar>
void funky(typename HasBar::Bar code, HasBar func) // <<< The code type is a nested type
{
   cout << "Has Bar: " << code << ", " << func() << endl;
}

int main()
{
   Foo foo;
   funky(3, []() { return "Lambda!"; });
   funky(3, foo);
   return 0;
}

Printing:

Generic: 3, Lambda!
Has Bar: 3, Foo!

However, it does not compile on gcc/clang, complaining:

 In function 'int main()':
27:16: error: call of overloaded 'funky(int, Foo&)' is ambiguous
27:16: note: candidates are:
12:6: note: void funky(CodeType, Callable) [with Callable = Foo; CodeType = int]
18:6: note: void funky(typename HasBar::Bar, HasBar) [with HasBar = Foo; typename HasBar::Bar = int]

The ambiguity is resolved correctly by VS2015 (which does not mean it is the conforming thing to do).

How can I get this to compile and run correctly on Clang/gcc?
I thought of using std::enable_if but couldn't get it to do what I want (I most likely used it incorrectly). If this is the way to go, how should it be used to solve this ambiguity?

UPDATE:
Adding typename HasBar::Bar to the template params gets gcc/Clang to build and run the code correctly:

template <typename HasBar, typename HasBar::Bar>
void funky(typename HasBar::Bar code, HasBar func) // <<< The code type is a nested type
{
   cout << "Has Bar: " << code << ", " << func() << endl;
}

This seems to tell the compiler that there is a second, non-type template parameter value (unused in the function code) that is of type typename HasBar::Bar. If typename HasBar::Bar does not exist, SFINAE will remove this function from the overload set and the generic form will be chosen.

However, when it does exist, I do not know why this function will take precedence over the first. I guess because it is more specialized - although the specialization isn't used in the code itself. However, in this case, it was already more specialized even before the new param!

However, in this case VS2015 always chooses the generic form giving the wrong answer!

Is there some syntax (and/or workaround) that will work in all cases?

like image 531
Adi Shavit Avatar asked May 17 '16 12:05

Adi Shavit


People also ask

What does template typename t mean?

template <typename T> ... This means exactly the same thing as the previous instance. The typename and class keywords can be used interchangeably to state that a template parameter is a type variable (as opposed to a non-type template parameter).

How is a template class different from a template function?

For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.

What is template type parameter?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

How template works in c++?

Templates in C++? It is a simple yet powerful tool that acts as a blueprint for creating generic functions or classes. While using a template in C++, you pass a data type as a parameter. Thus, instead of maintaining multiple codes, you have to write one code and give the data type you want to use.


1 Answers

13 minutes later... answering myself. [NOT] Solved!

Adding typename HasBar::Bar to the template params did the trick:

template <typename HasBar, typename HasBar::Bar>
void funky(typename HasBar::Bar code, HasBar func) // <<< The code type is a nested type
{
   cout << "Has Bar: " << code << ", " << func() << endl;
}

This seems to tell the compiler that there is a second, non-type template parameter value (unused in the function code) that is of type typename HasBar::Bar. If typename HasBar::Bar does not exist, SFINAE will remove this function from the overload set and the generic form will be chosen.

However, when it does exist, I do not know why this function will take precedence over the first. I guess because it is more specialized - although the specialization isn't used in the code itself.
However, in this case, it was already more specialized even before the new param!

like image 152
Adi Shavit Avatar answered Sep 22 '22 17:09

Adi Shavit