How to refer to a doubly-templated free function in C++ - c++

I have a templated class within which I define free functions taking references to that templated class. These free functions are also templated on a different parameter.
From outside the class I can call the free functions. However, I cannot find the correct syntax for one free function to call another.
Quick example:
template<typename T> class Foo {
template<typename S>
friend S f(const Foo &) { return S(); }
template<typename S>
friend S g(const Foo &s) {
return f(s); // See below, when instantiated, yields 'no matching function for call to f(const Foo &)'
}
};
float test1() {
Foo<int> o;
return f<float>(o); // Compiles
}
float test2() {
Foo<int> o;
return g<float>(o); // Fails to compile as line above errors
}
(c.f. this link too)
It seems by the point of the call to f(s) within g(), the outermost template has been lost. How might I re-specify the T in the call to f? I have checked on GCC4.7, 4.8, clang 3.2 all with equivalent errors.

When you call f(s) you need to specify the template parameter S because it can't be deduced from the argument s.
But if you change it to f<S>(s) (assuming you meant to call it with the same template argument S as g was called with) then you inhibit ADL, and the only way a friend function defined at class scope can be found is by ADL. So you need to add a declaration of f to the global namespace, so that the call in g can find it.
So to make it work you need to add these declarations before Foo
template<typename T> class Foo;
template<typename S, typename T>
S f(const Foo<T> &);
template<typename S, typename T>
S g(const Foo<T> &);
and change the call in g to be f<S>(s) or something else like f<x>(s)

Related

How do I add a template specialization when for a generic method on a generic class when the two types are equal?

I'm trying to add in a specialization where the generic type of method and class agree, but I haven't been able to figure out exactly how to specify the template instantiation (if it is even possible).
My best guess would be something like the following (though it obviously doesn't compile):
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT>
void Bar(MethodT arg)
{
}
};
template<typename T>
template<>
void Foo<T>::Bar(T arg)
{
x = arg;
}
As is usually the case when considering function template specialization, an overload can handle it:
template<typename MethodT>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
When you call Bar, one of the candidates will be a function template specialization and one won't. Think of the class template as stamping out real, concrete member functions where possible when it's instantiated. There's a rule pretty late in overload resolution to prefer the one that isn't a function template specialization if it's a tie up to that point.
What you end up with is the second overload being called when there's an "exact match" in types (which allows for a difference in top-level const). If exact matches are too narrow, you can restrict the first overload to widen the second:
// Allow the other overload to win in cases like Foo<int>{}.Bar(0.0).
// std::enable_if works as well before C++20.
template<typename MethodT>
void Bar(MethodT arg) requires (not std::convertible_to<MethodT, ClassT>)
{
}
As discussed in the comments, it's not possible to do this with template specialization. However, something similar can be accomplished by using std::enable_if_t and
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT,
typename = std::enable_if_t<!std::is_same<ClassT, MethodT>::value>>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
};
std::enable_if_t will only return a valid type when the input type arg is true. Therefore, the template substitution will fail when MethodT and ClassT are the same type, but the non-template overload will not fail. The template substitution failure is ok under SFINAE.

How to make class template argument deduction work inside the class itself? [duplicate]

Consider following code:
struct A {};
template <typename T> struct B
{
B(T) {}
auto foo() {return B(A{});} // error: no matching function for call to 'B<int>::B(A)'
};
auto foo() {return B(A{});} // compiles
int main()
{
foo();
B b(0);
b.foo();
}
Try it live
I understand why B::foo() doesn't compile: Inside of struct B<T>, B (as an injected-class-name) means B<T> unless it's explicitly used as a template. Which in this case prevents class template argument deduction.
Let's say I can't do auto foo() {return B<A>(A{});} since my actual code relies on slightly elaborate user-provided deduction guides.
The question is: How do I force class template argument deduction when constructing B inside of B::foo?
I hope I'm not missing something obvious.
You qualify it so that it's not the injected-class-name.
auto foo() {return ::B(A{});}
Another option is to use a function to do the type deduction for you.
template <typename T> B<T> make_b(T t) { return B<T>(t); }
and use
auto foo() {return make_b(A{});}

Is it possible to invoke an injected friend template function?

To be consistent with other (non-template) functions in a class I wanted to define and invoke a friend template function.
I can define it with no problem (see function t below).
namespace ns{
struct S{
void m() const{}
friend void f(S const&){}
template<class T>
friend void t(S const&){}
};
template<class T>
void t2(S const& s){}
}
However later I am not able to invoke this t function in any way?
int main(){
ns::S s;
s.m();
f(s);
// t<int>(s); // error: ‘t’ was not declared in this scope (I was expecting this to work)
// ns::t<int>(s); // error: ‘t’ is not a member of ‘ns’
// ns::S::t<int>(s); // error: ‘t’ is not a member of ‘ns::S’
}
Even if it is not possible at all, I am surprised that I am allowed to define it.
I tested this with gcc 8 and clang 7.
What you need for this to work are a couple of forward declarations.
The below two lines of code should come before the namespace ns.
struct S; //Needed because S is used as a parameter in the function template
template<class T> void t(S const&);
And then this form of call will work inside main.
t<int>(s);
See demo here.

Overloaded function is not always selected before template instantiation

I have created some code that uses a function template and an overload (not a specialization) of that function. When I call the function with an object of a class that is derived from the parameter in the it uses the template resulting in a compile error. I have been reading http://www.gotw.ca/publications/mill17.htm
and I had the impression that overloaded functions would always have preference above templates. I have created a similar do-nothing example:
class ITest
{
public:
virtual void foo()=0;
};
class TheTest:public ITest
{
public:
virtual void foo()
{
}
};
class test
{
public:
template<typename T>
void handle(T par)
{
par++;
}
void handle(ITest &t)
{
t.foo();
}
};
void TestThem()
{
test t;
t.handle(2);
t.handle(3.0);
TheTest t2;
t.handle(t2);
}
I would expect t.handle(t2) to call the overloaded void handle(ITest &t) since TheTest is derived from ITest. However the compiler selects the template which generates an error.
When I change void handle(ITest &t) to void handle(TheTest &t) it compiles fine.
I fixed it by removing the template function and overloading for all used types, but this is cumbersome since they all do exactly the same thing.
I had the impression that overloaded functions would always have preference above templates.
This is true, but only when the non template function and template function are equally good. In that case the non template function is used.
In this case though they are not both equally good. t2 is a TheTest, when overload resolution runs it finds void handle(ITest &t) and void handle(TheTest par) (I've instantiated the template here). Sine the template version will give an exact match it is a better function and is chosen.
The way to fix this is to constrain the template to only work for types that aren't derived from ITest. If you change the template function to
template<typename T, std::enable_if_t<!std::is_base_of_v<ITest, T>, bool> = true>
void handle(T par)
{
par++;
}
Then it will only be called for types that don't derive from ITest. You can see it working in this live example.

C++ template gotchas

just now I had to dig through the website to find out why template class template member function was giving syntax errors:
template<class C> class F00 {
template<typename T> bar();
};
...
Foo<C> f;
f.bar<T>(); // syntax error here
I now realize that template brackets are treated as relational operators. To do what was intended the following bizarre syntax is needed, cf Templates: template function not playing well with class's template member function:
f.template bar<T>();
what other bizarre aspects and gotcha of C++/C++ templates you have encountered that were not something that you would consider to be common knowledge?
I got tripped up the first time I inherited a templated class from another templated class:
template<typename T>
class Base {
int a;
};
template<typename T>
class Derived : public Base<T> {
void func() {
a++; // error! 'a' has not been declared
}
};
The problem is that the compiler doesn't know if Base<T> is going to be the default template or a specialized one. A specialized version may not have int a as a member, so the compiler doesn't assume that it's available. But you can tell the compiler that it's going to be there with the using directive:
template<typename T>
class Derived : public Base<T> {
using Base<T>::a;
void func() {
a++; // OK!
}
};
Alternatively, you can make it explicit that you are using a member of T:
void func {
T::a++; // OK!
}
This one got me upset back then:
#include <vector>
using std::vector;
struct foo {
template<typename U>
void vector();
};
int main() {
foo f;
f.vector<int>(); // ambiguous!
}
The last line in main is ambiguous, because the compiler not only looks up vector within foo, but also as an unqualified name starting from within main. So it finds both std::vector and foo::vector. To fix this, you have to write
f.foo::vector<int>();
GCC does not care about that, and accepts the above code by doing the intuitive thing (calling the member), other compilers do better and warn like comeau:
"ComeauTest.c", line 13: warning: ambiguous class member reference -- function
template "foo::vector" (declared at line 8) used in preference to
class template "std::vector" (declared at line 163 of
"stl_vector.h")
f.vector<int>(); // ambiguous!
The star of questions about templates here on SO: the missing typename!
template <typename T>
class vector
{
public:
typedef T * iterator;
...
};
template <typename T>
void func()
{
vector<T>::iterator it; // this is not correct!
typename vector<T>::iterator it2; // this is correct.
}
The problem here is that vector<T>::iterator is a dependent name: it depends on a template parameter. As a consequence, the compiler does not know that iterator designates a type; we need to tell him with the typename keyword.
The same goes for template inner classes or template member/static functions: they must be disambiguated using the template keyword, as noted in the OP.
template <typename T>
void func()
{
T::staticTemplateFunc<int>(); // ambiguous
T::template staticTemplateFunc<int>(); // ok
T t;
t.memberTemplateFunc<int>(); // ambiguous
t.template memberTemplateFunc<int>(); // ok
}
Out of scope class member function definition:
template <typename T>
class List { // a namespace scope class template
public:
template <typename T2> // a member function template
List (List<T2> const&); // (constructor)
…
};
template <typename T>
template <typename T2>
List<T>::List (List<T2> const& b) // an out-of-class member function
{ // template definition
…
}