Why doesn't that template function compile? - c++

This is a pretty short snippet that just won’t compile with g++ 4.7.1 (it won’t compile either with gcc 4.6.3 by the way).
#include <iostream>
template<typename T>
struct Foo
{
template<typename U>
friend std::ostream& operator<<(Foo&, U&);
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u)
{
std::cout << u;
return std::cout;
}
int main()
{
Foo<int> f;
f << "bar";
return 0;
}
And this is what gcc 4.7.1 outputs (4.6.3 says almost the same thing).
/tmp/ccNWJW6X.o: In function main': main.cpp:(.text+0x15): undefined
reference tostd::basic_ostream >&
operator<< (Foo&, char const (&) [4])' collect2:
ld returned 1 exit status
Anyone could explain why?
EDIT
I also tried with clang 3.1, and it says exactly the same thing.

Friendship with templates can be a bit complicated... Lets see what your code does:
template<typename T>
struct Foo {
template<typename U>
friend std::ostream& operator<<(Foo&, U&); // [1]
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u) { // [2]
std::cout << u;
return std::cout;
}
When you instantiate Foo with a type, for example int the friend declaration in [1] declares a template function:
template <typename U>
std::ostream& operator<<(Foo<int>&,U&);
But that function does not exist anywhere, what you are providing in [2] is a template that takes two arguments:
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u);
The key point is that the friend declaration is processed while the template is being instantiated, and at that time Foo represents the type obtained with the current instantiation.
There are different options for what you want to do, the simplest is changing the friend declaration to:
template<typename W, typename U>
friend std::ostream& operator<<(Foo<W> foo, U& u);
Which declares a template taking two arguments (both W and U are unbound here), and matches your definition at namespace level.
Another option is defining the friend function inside the class template definition, in which case you can maintain the original signature. For more information on the different alternatives, take a look at this other answer

You did not actually write the output operator<< for Foo
Notice the signatures for the two functions are very different

Related

operator== for classes inside a variadic template class

I have the following class structure:
template <typename...>
class SomeClass {
public:
class Foo { };
class Bar { };
};
I need to define operator== for SomeClass<Ts...>::Foo and SomeClass<Ts...>::Bar and I need to make it a friend of both Foo and Bar. The closest I got this to working is the following:
template <typename...>
class SomeClass {
public:
class Bar;
class Foo {
friend bool operator==(const Foo&, const Bar&) {
return true;
}
};
class Bar {
friend bool operator==(const Foo&, const Bar&);
};
};
Then I do:
SomeClass<int, double>::Foo foo;
SomeClass<int, double>::Bar bar;
foo == bar;
This compiles and works fine except for the fact that gcc gives me the warning:
warning: friend declaration `bool operator==(const SomeClass<Args>::Foo&, const SomeClass<Args>::Bar&)` declares a non-template function
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
I kind of understand why this happens (operator== indeed depends on the template parameters of SomeClass), but how do I get rid of it? Adding <> to the second friend declaration, as suggested, only breaks compilation: the friending is no longer recognized and the compiler complaints about access to a private member.
I tried to modify the code according to the template friend declaration guide, but that only made things worse:
template <typename...>
class SomeClass;
template <typename... Args>
bool operator==(const typename SomeClass<Args...>::Foo&,
const typename SomeClass<Args...>::Bar&);
template <typename... Args>
class SomeClass {
public:
class Bar;
class Foo {
friend bool operator==<Args...>(const Foo&, const Bar&);
};
class Bar {
friend bool operator==<Args...>(const Foo&, const Bar&);
};
};
template <typename... Args>
bool operator==(const typename SomeClass<Args...>::Foo&,
const typename SomeClass<Args...>::Bar&) {
return true;
}
Now the weirdest thing happens when I call foo == bar:
error: no match for ‘operator==’ (operand types are ‘SomeClass<int, double>::Foo’ and ‘SomeClass<int, double>::Bar’)
note: candidate: ‘bool operator==(const typename SomeClass<Args ...>::Foo&, const typename SomeClass<Args ...>::Bar&) [with Args = {}; typename SomeClass<Args ...>::Foo = SomeClass<>::Foo; typename SomeClass<Args ...>::Bar = SomeClass<>::Bar]’ (reversed)
note: no known conversion for argument 1 from ‘SomeClass<int, double>::Bar’ to ‘const SomeClass<>::Foo&’
I.e. for some weird reason it tries to call the template specification with an empty arguments list and fails. Changing the operator type (to operator+) doesn't help (my idea was, this had something to do with C++20 new rules for comparison operators, but no).
So the questions I have are:
What's going on? I'm kind of confused, especially by the second part: why would the compiler try to call the operator for an empty parameter pack?
How do I avoid the warning in the first solution?
How do I define the operator outside of the class definition properly?
Ok, here are the results of some research I did.
In case of the friend in-class definition, according to cppreference, what it does is it generates non-template overloads of operator==. Thus, it is perfectly fine to reference them from the second friend declaration, and the code itself is correct. This is also why adding "<>" breaks the befriending: the overloads generated by the first definition are not template functions, while the second definition with the "<>" added now refers to some template overload that isn't definded.
Both Clang and MSVC compile that code without any problems, so the warning seems to be a GCC-only thing. I think, it is ok to suppress it.
The alternative would be to make the operator into a template, but unfortunately doing this properly for the parent class template parameters is impossible. This won't compile (see below why):
template <typename...>
class SomeClass {
public:
class Bar;
class Foo {
template <typename... Ts>
friend bool operator==(const typename SomeClass<Ts...>::Foo&,
const typename SomeClass<Ts...>::Bar&) {
return true;
}
};
class Bar {
template <typename... Ts>
friend bool operator==(const typename SomeClass<Ts...>::Foo&,
const typename SomeClass<Ts...>::Bar&);
};
};
The real alternative here is to trick GCC by making the operator template with some obsolete arguments, that could be deduced. And since default template arguments are forbidden in friend declarations, the only other hack I can think of is to make the operator variadic and use the fact that a trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack. This works and emits no warnings in GCC:
template <typename...>
class SomeClass {
public:
class Bar;
class Foo {
template <typename...>
friend bool operator==(const Foo&, const Bar&) {
return true;
}
};
class Bar {
template <typename...>
friend bool operator==(const Foo&, const Bar&);
};
};
I really see no reason in doing that now, so the best thing to do is to pretend that you never saw this, and that I never wrote this.
Now, the second part, the outside definition. The problem here is that, as it turns out, there is no way in C++ to deduce template arguments (pack or not) of a parent class from a nested class.
I.e. a thing like this cannot possibly work:
template <typename T>
void foo(typename Parent<T>::Child) { }
foo(Parent<int>::Child{});
There even was a proposal to resolve this issue, but it was declined.
Defining operator== as a template function in my example above has exactly this problem: once the operator is called for SomeClass<int, double>::Foo and SomeClass<int, double>::Bar, the argument deduction is impossible (though the definition itself is correct), thus the compilation fails. This is also the answer to my first question: since the deduction is impossible, a parameter pack is deduced as an empty pack. Not at all what I intended.
The only workaround I came up with is to define the operator for the template non-nested arguments, and then restrict them. I am going to use C++20 concepts for this, although I think a SFINAE solution could be just as fine.
This works exactly as intended:
template <typename...>
class SomeClass;
template <typename T>
constexpr bool IsSomeClass = false;
template <typename... Args>
constexpr bool IsSomeClass<SomeClass<Args...>> = true;
template <typename T, typename U>
concept IsProperFooBar = requires {
typename T::Parent;
typename U::Parent;
requires std::same_as<typename T::Parent, typename U::Parent>;
requires IsSomeClass<typename T::Parent>;
requires std::same_as<std::decay_t<T>, typename T::Parent::Foo>;
requires std::same_as<std::decay_t<U>, typename U::Parent::Bar>;
};
template <typename T, typename U> requires IsProperFooBar<T, U>
bool operator==(const T&, const U&);
template <typename... Args>
class SomeClass {
public:
class Foo {
public:
using Parent = SomeClass;
private:
template <typename T, typename U> requires IsProperFooBar<T, U>
friend bool operator==(const T&, const U&);
};
class Bar {
public:
using Parent = SomeClass;
private:
template <typename T, typename U> requires IsProperFooBar<T, U>
friend bool operator==(const T&, const U&);
};
};
template <typename T, typename U> requires IsProperFooBar<T, U>
bool operator==(const T&, const U&) {
return true;
}
The only issue I see with this solution is that there is now a universal template operator==, so the concept will be checked every time you call == for anything, which may slow down compilation and emit unnecessary concept-not-satisfied diagnostic messages for other things.
On the plus side, there is no need in forward-declaring class Bar anymore, so at least I got that going for me, which is nice :-)
I got this solution with operator=='s definition outside the class.
There is no warning now, but I had to remove the friend qualifier. In order to access Foo's private methods from Bar's functions, declare Bar a friend of Foo (inside Foo). Do likewise for Bar if you want to access Bar's members from within Foo's methods.
Perhaps its a step forward. Hopes this helps.
#include <iostream>
template <typename... Args>
class SomeClass {
public:
class Bar;
class Foo;
class Foo {
public:
friend Bar;
bool operator==(const Bar&) const;
void do_something_to_Bar(Bar&);
private:
int _foo;
};
class Bar {
public:
friend Foo;
bool operator==(const Foo&) const;
void do_something_to_Foo(Foo&);
private:
int _bar;
};
};
template<typename... Args>
bool SomeClass<Args...>::Foo::operator== (const Bar& b) const {
return b._bar == _foo;
}
template<typename... Args>
bool SomeClass<Args...>::Bar::operator== (const Foo& f) const {
return f == *this;
}
template<typename... Args>
void SomeClass<Args...>::Bar::do_something_to_Foo(Foo& f) {
f._foo = 4;
_bar = 4;
}
template<typename... Args>
void SomeClass<Args...>::Foo::do_something_to_Bar(Bar& b) {
b._bar = 4;
_foo = 3;
}
int main() {
SomeClass<int,float>::Foo foo;
SomeClass<int,float>::Bar bar;
foo.do_something_to_Bar(bar);
std::cout << (foo == bar) << '\n';
std::cout << (bar == foo) << '\n';
bar.do_something_to_Foo(foo);
std::cout << (foo == bar) << '\n';
std::cout << (bar == foo) << '\n';
}
The definition of SomeClass<Args...>::Bar::operator== does not redefine the operator, but rather uses the definition of SomeClass<Args...>::Foo::operator==.

Why do we not find the right operator overload when using the macro?

I am writing a class template which takes an arbitrary function pointer as non-type template argument. I would like to use
template <auto F> struct Foo;
but my compiler (MSVC 2017.5) does not support auto in the template parameter list (even though it supports many C++17 features). So I wrote a hack around this somehow like this:
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { ... };
#define FOO(func) Foo<decltype(func), func>
I implemented a streaming operator (for QDebug or any other text stream) like this:
template <typename T, Func<T> F>
QDebug &operator <<(QDebug &out, const FOO(F) &foo)
{
...
return out;
}
But my main code cannot find the right operator<< overload:
void func(int) { ... }
...
FOO(&func) foo;
qDebug() << foo; // error
Surprisingly, everything works when defining the operator like
QDebug &operator <<(QDebug &out, const Foo<Func<T>, F> &foo)
// ^^^^^^^^^^^^^^^
It seems that Func<T> from this last line and decltype<F> from the macro do not give the same type. But I checked this and std::is_same_v<Func<int>, decltype(&func)> gives true. I cannot think why using the macro FOO gives me any other compile time behavior as when not using it.
A minimal working example:
#include <iostream>
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { };
#define FOO(func) Foo<decltype(func), func>
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const FOO(F) &foo)
// std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
{
return out;
}
void func(int);
int main(int argc, char **argv)
{
FOO(&func) foo;
std::cout << foo << std::endl; // error
}
A workaround:
template <typename T, Func<T> F>
struct Foo<Func<T>, F> {
friend QDebug &operator <<(QDebug &out, const Foo &foo){
...
return out;
}
};
As part of the template auto paper, we also got a new deduction rule in [temp.deduct.type]:
When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value.
This rule allows the following example to work in C++17, because we can deduce T from the type of V:
template <typename T, T V>
struct constant { };
template <typename T, T V>
void foo(constant<decltype(V), V> ) { }
int main() {
foo(constant<int, 4>{});
}
In C++14 and earlier, this example is ill-formed because T isn't deduced. It is precisely this behavior that you are trying to use (implicitly) when you use that macro, which expands into:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<decltype(F), F> &foo);
You are trying to deduce T from F. Since MSVC doesn't support template auto yet, it's perhaps unsurprising that it also doesn't support other parts of the machinery requires to make template auto work. And that's the difference between the macro and the non-macro alternative, which can simply deduce T:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
So the simple solution is to just... don't use a macro, since you have a working form, even it's more verbose. A longer solution is to use Yakk's answer which side-steps the whole deduction question entirely.
When no macro is used second template parameter F can be deduced from second function argument foo. When macro is used second template parameter F can not be deduced from second function argument because it will appear inside of decltype: Foo<decltype(F), F> & foo. Your code can be simplified to
template<typename T>
void f(decltype(T) v){}
int v{};
f(v);
Compiler knowns the type of argument (int) however template parameter T can not be deduced from known type of argument because when used inside of decltype T must be known at advance.

Non-member operator overloading of inner class templates

I prefer to write definitions for class and function templates in a separate file which is automatically included after the "public" header. However, I've come to an interesting case where it looks like I can't do that.
template <typename T>
class Outer
{
public:
template <typename U>
class Inner
{
friend bool operator ==(const Inner& lhs, const Inner& rhs);
};
};
using Type = Outer<int>::Inner<short>;
int main()
{
Type a;
Type b;
a == b;
}
Is it possible to write definition of operator== separately that will work for any T and U?
For a particular specialization, yes:
template <typename T>
class Outer
{
public:
template <typename U>
class Inner
{
int x = 42;
friend bool operator ==(const Inner& lhs, const Inner& rhs);
};
};
using Type = Outer<int>::Inner<short>;
bool operator ==(const Type& lhs, const Type& rhs) {
return lhs.x == rhs.x;
}
int main()
{
Type a;
Type b;
a == b;
}
In your example, each specialization of the template befriends a non-template function that takes that particular specialization as parameters. You could define this function in-class (and then it will be stamped out every time the template is instantiated), or you could define it out-of-class - but then you would have to define one for every specialization you ever use.
As Igor Tandetnik points out, your example declares a non-template friend function, which you'll have to overload for each template instantiation, which is one reason friend functions are allowed to be defined inline (so they can be generated by the template without having to be templates themselves).
If you want to define the friend function as a template, here is the closest I was able to come up with:
template <typename T>
struct Outer {
template <typename U>
struct Inner;
};
template<typename T, typename U>
bool operator==( typename Outer<T>::template Inner<U> const &, typename Outer<T>::template Inner<U> const & );
template <typename T>
template <typename U>
struct Outer<T>::Inner {
friend bool operator==<T,U>(Inner const &, Inner const &);
};
template<typename T, typename U>
bool operator==( typename Outer<T>::template Inner<U> const &, typename Outer<T>::template Inner<U> const & ) {
return true;
}
// I switched this out, because my gcc-4.6 doesn't
// understand "using" aliases like this yet:
typedef Outer<int>::Inner<short> Type;
int main() {
Type a;
Type b;
operator==<int,short>( a, b );
}
Unfortunately, you'll notice the call site of this operator function is very awkward: operator==<int,short>( a, b ). I believe defining a function template on a nested class template like this disables (or at least interferes with) argument deduction, so you have to specify the template parameters explicitly (which means calling it as a function rather than in operator form). This is why the inline friend definition is so convenient. If you really want to define your operator=='s code separately, I'd recommend defining the friend inline to call another function template (with the proper template arguments), which you can then define out-of-line as a free function.

Making certain specializations of a function template friend

Suppose I have
template<class T>
void f(T t);
and
template<class T>
class X
{
};
If I want only f<T> to be friend of X<T>, I declare:
template<class T>
class X
{
friend void f<>(T t);
};
Now suppose f is declared like this:
template<class T, class U>
void f(T t, U u);
I want to declare the following: for any type U f<T, U> is friend of X<T>. So I want f<int, char> to be friend of X<int>, but I don't want f<char, int> to be frient of X<int>. Is this possible? The following doesn't seem to compile
template<class T>
class X
{
template <class U>
friend void f<>(T t, U, u);
};
Please note that I'm aware how to declare the whole template as friend.
Apparently, there is no way in current C++ to do what I'm after.
[EDIT]
I had taken this idea from a website which stated this was possible,
but not properly tested it. As it turns out, this does not work,
so please disregard this suggestion.
As far as I can tell, what OP wants to do is not possible in c++...
[/EDIT]
The code as you posted it would partially specialize the function f, however this is not allowed.
To fix it, remove the empty angle brackets from the friend declaration:
template<class T>
class X
{
template <class U>
friend void f(T t, U, u);
};

Overload resolution with universal references

I have a function which can accept any type by universal reference, and would like to overload it for specific types (some of which are templated themselves, although I don't think that's important here). Unfortunately I can't quite seem to to get the overloads to be resolved in the right order.
I would have presumed that the second declaration of foo would be preferred as it's more specific (less templated), although it looks like my understanding of overload resolution is lacking somewhat. Interestingly changing the second declaration to take X by value makes it print "good, good" and making it take X by non-const reference makes it print "bad, good". Obviously removing the first declaration entirely makes it return "good, good", as there's no other choice.
So why does this happen? And most importantly, if the following code doesn't work, how can you overload a function with this signature?
#include <iostream>
#include <string>
class X {};
template<typename T>
inline std::string foo(T && rhs) {
return "bad";
}
inline std::string foo(const X & rhs) {
return "good";
}
int main() {
std::cout << foo(X()) << std::endl;
X x;
std::cout << foo(x) << std::endl;
return 0;
}
Edit:
Maybe a more roundabout solution to this is to do it indirectly. Get rid of the first form of foo and use SFINAE to check if a valid overload exists, it it doesn't then call foo_fallback.
To answer your question.comment to Kerre's answer, you could try to use SFINAE:
#include <type_traits>
#include <string>
template <class T>
struct HasFooImpl_ {
template <typename C>
static std::true_type test(decltype(fooImpl(std::declval<C>()))*);
template <typename C>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
};
template <typename T>
using HasFooImpl = typename HasFooImpl_<T>::type;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}
You'd have to implement a function fooImpl for any type that you don't want to be handled genericly.
The implementation was a bit tricky, I tried just enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value first, but for the fallback the !is_same<>::value gave me compiler errors, because it tried to instantiate the decltype as well.
This implementation has one caveat that you might or might not want to use: if T is convertible to some other type that has a fooImpl defined, that conversion will kick in.
You can see the whole thing in action here: http://ideone.com/3Tjtvj
Update:
if you don't want to allow type conversions, it actually gets easier:
#include <type_traits>
#include <string>
template <typename T> void fooImpl(T);
template <typename T>
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}
See http://ideone.com/miaoop
The conversion from X to const X is considered worse than the direct match of the templated overload with T = X or T = X &.