How do you make a fold expression with custom operator <<? - c++

I am trying to make a print function based on parameter pack and fold expression. My implementation fails to compile on clang 10.0. Here is the code:
#include <iostream>
#include <set>
std::ostream &operator<<(std::ostream &strm, const std::set<int> &set) {
strm << set.size();
return strm;
}
template <typename... Args> void Print(std::ostream &strm, Args &&... args) {
(strm << ... << args);
}
int main(int, char **) {
std::set<int> my_set;
Print(std::cout, my_set);
}
and here is the compiler output
#1 with x86-64 clang 10.0.0
<source>:10:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
(strm << ... << args);
^
<source>:15:3: note: in instantiation of function template specialization 'Print<std::set<int, std::less<int>, std::allocator<int> > &>' requested here
Print(std::cout, my_set);
^
<source>:4:15: note: 'operator<<' should be declared prior to the call site
std::ostream &operator<<(std::ostream &strm, const std::set<int> &set) {
^
1 error generated.
Compiler returned: 1
The same code compiles with gcc 10.1. Is this a compiler bug (or missing feature), or is my implementation incorrect?

Argument-dependent lookup is not helping you here.
The error message is:
:10:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
So it's looking for an operator<< to call:
It looks in the definition of std::set - nope, none there.
It looks in namespace std (because that's where std::set lives) - nope, not there, either.
Should it look in the global namespace? I don't know.
Clearly gcc and clang have different opinions about that.

Related

<< operator overloading and template specialization

As part of study purpose, I was just playing with template specialization together with operator overloading
#include <iostream>
template<class T>
void operator<<(std::ostream& COUT,T val)
{
std::operator<<(COUT,val); //works for basic data types
}
//specializing for const char*
template<>
void operator<<(std::ostream& COUT,const char* val)
{
std::operator<<(COUT,val);
}
int main()
{
std::cout<<"samplestring"; //getting error here
return 0;
}
I am getting following error which I cannot figure out.
<source>:17:14: error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'const char [13]')
17 | std::cout<<"samplestring";
| ~~~~~~~~~^~~~~~~~~~~~~~~~
| | |
| | const char [13]
| std::ostream {aka std::basic_ostream<char>}
what is the problem here
Thank you..!
The problem is there is already operator<<(std::ostream& COUT,const char* val) defined by the standard library.
So now there are two symbols
std::operator<<(std::ostream& ostream,const char* val);
operator<<(std::ostream& COUT,const char* val); // YOURS
Because the symbols are different, there are no multiple-definition errors.
For a call std::cout << "Hello", the compiler searches for all operator<< functions currently defined at the call site. That looks like it is just your function, but the compiler also looks into all namespaces in which the passed arguments are defined. In this case it is std:: namespace and there the compiler finds the STL version. That is called argument dependent lookup(ADL).
Since both functions are templates and neither is more specialized than the other, the call is ambiguous.
Please do not use all capital letters as variable COUT, that is usually reserved for macros, with one-letter template parameters being exception T.
//works for basic data types
That is also not true, std::cout<<5; will NOT call your definition with T. Because there exists std::ostream::operator<<(int v) method. Same lookup happens but since this method is not a template, it is a better candidate and thus chosen. Your template function is never called.

fold expression and function name lookup

I am learning fold expressions in C++17. I have the following code
#include <iostream>
#include <vector>
namespace io {
template<typename T>
std::istream &operator>>(std::istream &in, std::vector<T> &vec) {
for (auto &x : vec)
in >> x;
return in;
}
template<class... Args> void scan(Args &... args) {
(std::cin >> ... >> args);
}
}// namespace io
int main() {
std::vector<int> s(1), t(1);
io::scan(s, t);
std::cout << s[0] << ' ' << t[0] << '\n';
}
Using GCC 9.3.0, the code compiles and runs correctly, but using Clang 10.0.0, the same code does not compile:
<source>:13:16: error: call to function 'operator>>' that is neither visible in the template definition nor found by argument-dependent lookup
(std::cin >> ... >> args);
^
<source>:19:9: note: in instantiation of function template specialization 'io::scan<std::vector<int, std::allocator<int> >, std::vector<int, std::allocator<int> > >' requested here
io::scan(s, t);
^
<source>:6:15: note: 'operator>>' should be declared prior to the call site
std::istream &operator>>(std::istream &in, std::vector<T> &vec) {
^
1 error generated.
Why clang rejets the code but gcc accepts it?
This was a Clang bug. Clang versions 11 and earlier did not properly implement two-phase name lookup for the operator in a fold expression, and would incorrectly perform the first-phase lookup from the lexical scope in which the instantiation of the fold-expression happened to be performed rather than doing the first-phase lookup from the context of the template definition.
I fixed this relatively recently (unfortunately not in time for the upcoming Clang 11 release), and the test case is now accepted by Clang trunk.

C++ function header matching: how does matching work when const and templates are both involved?

I had a templated function that I wished to call. This is (a trimmed-down version of) the header:
template <typename Item>
void print (shared_ptr<const MyContainer<Item>> stuff, ostream& out)
which I tried to call with a line like this:
print (make_shared<MyContainer<int>>(42), cerr);
But the compiler complained that there was no match. What confuses me is that the const mismatch is not a problem, because if I redeclare my function to omit the template it works:
void print (shared_ptr<const MyContainer<int>> stuff, ostream& out) //matches!
On the other hand, if I omit constness, the templated version does work:
template <typename Item>
void print (shared_ptr<MyContainer<Item>> stuff, ostream& out) //also matches!
But I should be able to write a function over const things and pass it a non-const value (which the function will then just not modify), right? Indeed, if I go back to non-managed pointers, the corresponding old way to write the header would have been
template <typename Item>
void print (const MyContainer<Item>* stuff, ostream& out)
and indeed then a call to
print (new MyContainer<int>(42), cerr); //yet another match!
once again just fine.
So, what is it about this particular cocktail of shared_ptr, templates, and const that causes the compiler to be unable to find the matching function? (Running g++ 8.2.1, and clang++ 7.0.1 seems to produce the same result.)
Concerning const-ness of pointee, std::shared_ptr behaves a bit different than raw-pointers.
A std::shared_ptr<T> is not the same as a std::shared_ptr<const T>. It's even not that compatible to allow an implicit conversion. (The error message in Daniels answer says this quite literally.)
It doesn't work for the same reason like in the following (counter) example:
template <typename T>
struct ContainerT {
T a;
ContainerT(T a): a(a) { }
ContainerT(const ContainerT&) = default;
ContainerT& operator=(const ContainerT&) = default;
};
int main()
{
ContainerT<int> a(42);
ContainerT<const int> b(a);
return 0;
}
Output:
g++ (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
main.cpp: In function 'int main()':
main.cpp:15:28: error: no matching function for call to 'ContainerT<const int>::ContainerT(ContainerT<int>&)'
ContainerT<const int> b(a);
^
main.cpp:8:3: note: candidate: 'constexpr ContainerT<T>::ContainerT(const ContainerT<T>&) [with T = const int]'
ContainerT(const ContainerT&) = default;
^~~~~~~~~~
main.cpp:8:3: note: no known conversion for argument 1 from 'ContainerT<int>' to 'const ContainerT<const int>&'
main.cpp:7:3: note: candidate: 'ContainerT<T>::ContainerT(T) [with T = const int]'
ContainerT(T a): a(a) { }
^~~~~~~~~~
main.cpp:7:3: note: no known conversion for argument 1 from 'ContainerT<int>' to 'int'
Live Demo on coliru
In the case of std::shared_ptr, there is a way to circumvent this issue
→ a std::const_pointer_cast can be used:
#include <iostream>
#include <memory>
template <typename T>
struct ContainerT {
T a;
ContainerT(T a): a(a) { }
};
template <typename T>
void print(std::shared_ptr<const ContainerT<T>> ref, std::ostream &out)
{
out << "print: '" << ref->a << "'\n";
}
int main()
{
print(std::make_shared<const ContainerT<int>>(42), std::cout);
print(std::const_pointer_cast<const ContainerT<int>>(std::make_shared<ContainerT<int>>(42)), std::cout);
return 0;
}
Output:
print: '42'
print: '42'
Live Demo on coliru
For convenience, the const-cast might be done in another function template:
#include <iostream>
#include <memory>
template <typename T>
struct ContainerT {
T a;
ContainerT(T a): a(a) { }
};
template <typename T>
void print(std::shared_ptr<const ContainerT<T>> ref, std::ostream &out)
{
out << "print const: '" << ref->a << "'\n";
}
template <typename T>
void print(std::shared_ptr<ContainerT<T>> ref, std::ostream &out)
{
out << "print non-const: ";
print(std::const_pointer_cast<const ContainerT<T>>(ref), out);
}
int main()
{
print(std::make_shared<const ContainerT<int>>(42), std::cout);
print(std::make_shared<ContainerT<int>>(42), std::cout);
return 0;
}
Output:
print const: '42'
print non-const: print const: '42'
Live Demo on coliru
Here is a simplified code:
template <typename T>
void f(std::shared_ptr<const std::vector<T>>) { }
void g(std::shared_ptr<const std::vector<int>>) { }
int main() {
f(std::make_shared<std::vector<int>>()); // ERROR
g(std::make_shared<std::vector<int>>()); // OK
}
To understand what happens, read the error message, e.g., the one printed by g++:
...
note: template argument deduction/substitution failed:
note: types 'const std::vector<T>' and 'std::vector<int>' have incompatible cv-qualifiers
It tells you that the problem is with template argument deduction/substitution. The C++ rules seemingly do not allow this type of deduction. (If I have some time, I will try to find a relevant part of the Standard).
However, you can skip the template argument deduction by providing an explicit template argument:
f<int>(std::make_shared<std::vector<int>>()); // OK

Template functions in namespace cause errors

Assume the following code:
#include <iostream>
template<typename T>
struct Link
{
Link(T&& val) : val(std::forward<T>(val)) {}
T val;
};
template<typename T>
std::ostream& operator<<(std::ostream& out, const Link<T>& link)
{
out << "Link(" << link.val << ")";
return out;
}
template<typename T>
auto MakeLink(T&& val) -> Link<T>
{
return {std::forward<T>(val)};
}
namespace Utils {
template<typename Any>
constexpr auto RemoveLinks(const Any& any) -> const Any&
{
return any;
}
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
{
return RemoveLinks(link.val);
}
} /* Utils */
int main()
{
int k = 10;
auto link = MakeLink(MakeLink(k));
std::cout << link << std::endl;
std::cout << Utils::RemoveLinks(link) << std::endl;
}
For some reason I can't understand, it generates the following compilation errors with g++-4.8:
/home/allan/Codes/expr.cpp: In instantiation of ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:88:32: required from ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = Link<int&>; decltype (Utils::RemoveLinks(link.val)) = const int&]’
/home/allan/Codes/expr.cpp:100:41: required from here
/home/allan/Codes/expr.cpp:88:32: error: invalid initialization of reference of type ‘const Link<int&>&’ from expression of type ‘const int’
return RemoveLinks(link.val);
^
/home/allan/Codes/expr.cpp:89:1: error: body of constexpr function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const Link<int&>&]’ not a return-statement
}
^
/home/allan/Codes/expr.cpp: In function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:89:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
while clang 3.3 gives:
test.cc:34:12: error: reference to type 'const Link<int &>' could not bind to an lvalue of type 'const int'
return RemoveLinks(link.val);
^~~~~~~~~~~~~~~~~~~~~
test.cc:46:25: note: in instantiation of function template specialization 'Utils::RemoveLinks<Link<int &> >' requested here
std::cout << Utils::RemoveLinks(link) << std::endl;
If, however, the namespace Utils is removed, then it compiles without errors (both gcc and clang), and execution outputs:
Link(Link(10))
10
Why defining those template functions (RemoveLinks) in a namespace causes such errors?
This problem is a result of an issue with the point of declaration (1) combined with dependent name lookup (2).
(1) In the declaration
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
the name RemoveLinks, or more precisely, this overload of RemoveLinks, is only visible after the complete declarator according to [basic.scope.pdecl]/1. The trailing-return-type is part of the declarator as per [dcl.decl]/4. Also see this answer.
(2) In the expression RemoveLinks(link.val), the name RemoveLinks is dependent as per [temp.dep]/1, as link.val is dependent.
If we now look up how dependent names are resolved, we find [temp.dep.res]:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
The first bullet doesn't find the second overload of RemoveLinks because of the point of declaration (1). The second one doesn't find the overload because the namespace Util is not associated with any argument. This is why putting everything in the global namespace or in the namespace Util works as expected (Live example).
For the same reason, using a qualified-id in the trailing-return-type (like -> decltype(Util::RemoveLinks(link.val)) doesn't help here.
I tried compiling the sample code above with GCC 4.8.1, with clang and also Intel icpc and got the same error messages as you.
I am able to get it to successfully compile without trouble if I revise the signature of the template specialization from:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
and make the return type as const. This might cause a compiler warning since the const there is meaningless, but that can be ignored. I tested it and it works fine for me with gcc, but not icpc or clang:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val)) const
I found the error message (with the original code) from Intel icpc to be the most informative:
code.cc(48): error: template instantiation resulted in unexpected function type of "auto (const Link<Link<int &>> &)->const int &" (the meaning of a name may have changed since the template declaration -- the type of the template is "auto (const Link<T> &)->decltype((<expression>))")
std::cout << Utils::RemoveLinks(link) << std::endl;
^
detected during instantiation of "Utils::RemoveLinks" based on template argument <Link<int &>> at line 48
Unfortunately, the above answer is more of a workaround for gcc rather than an answer to your question. I'll update this if I have anything more / better to add.
EDIT
It appears that decltype(RemoveLinks(link.val)) is actually following the recursion so that it returns int& rather than Link.
EDIT #2
There have been reported bugs in LLVM about crashes caused by decltype recursion problems. It seems that this is definitely a bug of sorts, but one that seems to be present in multiple implementations of C++.
The problem can be fixed quite easily if you create a an alias for type T in the link struct and have decltype refer to the alias rather than to the return type. This will eliminate the recursion. As follows:
template<typename T>
struct Link
{
Link(T&& val) : val(std::forward<T>(val)) {}
using value_type = T;
T val;
};
And then the RemoveLinks signature is changed accordingly to refer to this alias:
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(links.value_type)
This code successfully builds on all 3 compilers.
I will file some bug reports with the compilers to see if there's anything they can do about it.
Hope this helps.

g++ compiler error: couldn't deduce template parameter ‘_Funct’

I'm trying to use an ANSI C++ for_each statement to iterate over and print the elements of a standard vector. It works if I have the for_each call a non-overloaded function, but yields a compiler error if I have it call an overloaded function.
Here's a minimal test program to show where the compiler error occurs:
#include <algorithm>
#include <iostream>
#include <vector>
struct S {
char c;
int i;
};
std::vector<S> v;
void print_struct(int idx);
void print_struct(const struct S& s);
// f: a non-overloaded version of the preceding function.
void f(const struct S& s);
int main()
{
v.push_back((struct S){'a', 1});
v.push_back((struct S){'b', 2});
v.push_back((struct S){'c', 3});
for (unsigned int i = 0; i < v.size(); ++i)
print_struct(i);
/* ERROR! */
std::for_each(v.begin(), v.end(), print_struct);
/* WORKAROUND: */
std::for_each(v.begin(), v.end(), f);
return 0;
}
// print_struct: Print a struct by its index in vector v.
void print_struct(int idx)
{
std::cout << v[idx].c << ',' << v[idx].i << '\n';
}
// print_struct: Print a struct by reference.
void print_struct(const struct S& s)
{
std::cout << s.c << ',' << s.i << '\n';
}
// f: a non-overloaded version of the preceding function.
void f(const struct S& s)
{
std::cout << s.c << ',' << s.i << '\n';
}
I compiled this in openSUSE 12.2 using:
g++-4.7 -ansi -Wall for_each.cpp -o for_each
The full error message is:
for_each.cpp: In function ‘int main()’:
for_each.cpp:31:48: error: no matching function for call to ‘for_each(std::vector<S>::iterator, std::vector<S>::iterator, <unresolved overloaded function type>)’
for_each.cpp:31:48: note: candidate is:
In file included from /usr/include/c++/4.7/algorithm:63:0,
from for_each.cpp:5:
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template<class _IIter, class _Funct> _Funct std::for_each(_IIter, _IIter, _Funct)
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template argument deduction/substitution failed:
for_each.cpp:31:48: note: couldn't deduce template parameter ‘_Funct’
I don't see any search results for this particular error on Stack Overflow, or on the web generally. Any help would be appreciated.
A names refers to an overload set. You'll need to specify which overload you want:
std::for_each(v.begin(), v.end(), (void (&)(S const&)) print_struct);
Another approach is to use a polymorphic callable function object as a helper:
struct PrintStruct
{
template <typename T> void operator()(T const& v) const
{ return print_struct(v); }
};
int main()
{
PrintStruct helper;
std::vector<S> sv;
std::vector<int> iv;
// helper works for both:
std::for_each(sv.begin(), sv.end(), helper);
std::for_each(iv.begin(), iv.end(), helper);
std::for_each declaration looks like this:
template<class InputIter, class Func>
void for_each(InputIter first, InputIter last, Func func);
As you can see, it takes anything you give it as the third parameter. There is no restriction that it has to be a callable type of a certain signature or a callable type at all.
When dealing with overloaded functions, they're inherently ambiguous unless you give them some context to select the right one. In a call to an overloaded function, this context are the arguments you pass. When you need a pointer, however, you can't use arguments as a context, and the for_each parameter also doesn't count as a context, since it takes anything.
As an example of where a function parameter can be a valid context to select the right overload, see this:
// our overloads
void f(int){}
void f(double){}
typedef void (*funcptr_type)(int);
void g(funcptr_type){}
// ...
g(&f); // will select 'void f(int)' overload, since that's
// the only valid one given 'g's parameter
As you can see, you give a clear context here that helps the compiler select the right overload and not have it ambiguous. std::for_each's parameters do not give such a context, since they take anything.
There are two solutions:
manually provide the context either by
casting to the right function pointer type, or
using an intermediate variable of the right type and passing that
use a non-overloaded function that dispatches to an overloaded one (as you did with f)
Note that in C++11, you could also use a lambda for the second option:
std::for_each(v.begin(), v.end(), [](const S& s){ print_struct(s); });
Some notes on your code:
(struct S){'a', 1} is a compound literal and not standard C++
you don't need struct S in C++, only S suffices