I had some code that was failing to compile, which amounts to something
like what's shown below. After some digging around, I came across
paragraph 14.1 note 5, which states:
The top-level cv-qualifiers on the template-parameter are ignored
when determining its type.
My code looks like this:
#include <iostream>
#include <typeinfo>
class Bar {};
template<class T>
void Func(T t)
{
std::cout << typeid(T).name() << "\n";
}
template<class T>
void Func(const T& t)
{
std::cout << "const ref : " << typeid(T).name() << "\n";
}
int main()
{
Bar bar;
const Bar& constBar = bar;
Func(constBar);
return 0;
}
It gives this compilation error:
In function 'int main()'
error: call of overloaded 'Func(const Bar&)' is ambiguous
Can someone comment on the reasoning behind the this rule in the standard?
The problem with your code is that the function call is ambiguous. The const Bar & can match either the value or the const reference. G++ says:
xx.cpp:24: error: call of overloaded 'Func(const Bar&)' is ambiguous
This has nothing specifically to do with templates - you would get the same error if you overloaded a non-template function.
And as people have told you here time after time, you will not learn C++ by reading the Standard.
As you could have found easily yourself, this has nothing to do with templates. This
class Bar {};
void Func(Bar) {}
void Func(const Bar&) {}
int main()
{
Bar bar;
const Bar& constBar = bar;
Func(bar);
Func(constBar);
return 0;
}
gives the same errors.
The call is ambiguous because anything can match T or const T &.
Just try Func(0);: it will give the same error message.
Related
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
Question:
In the code below, template argument type deduction seems to fail for the first sample, but not for the second sample. I don't understand why the first sample fails to deduce T = char. I would think that T can be deduced when converting from "foo" to std::bacis_string<T>, but even if that didn't work, I provide the second function argument which, I would think, would clearly constrain T to char. Why does it fail?
Does not work:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string bar = "bar";
print("foo", bar);
}
Error:
string.cpp:14:5: error: no matching function for call to 'print'
print("foo", bar);
^~~~~
string.cpp:6:6: note: candidate template ignored: could not match
'basic_string<type-parameter-0-0, char_traits<type-parameter-0-0>,
allocator<type-parameter-0-0> >' against 'char const[4]'
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
^
1 error generated.
Works:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string foo = "foo";
std::string bar = "bar";
print(foo, bar);
}
The problem is a conversion is required here. To deduce T, the compiler would have to inspect all possible instantiations of std::basic_string and see which of them can be constructed from a const char* (or actually const char (&)[4]). That's of course not possible, as there's infinitely many of them. The reason why it has to inspect all and cannot just scan the primary template definition for constructors taking const char* or const char(&)[4] is that for some T, std::basic_string<T> could be partially or completely specialised, and the members of those specialisations have no relationship to the members of the primary template.
Here's the short version of an answer.
The compiler has char const[] and is looking to convert that to std::basic_string<T>. How does it work out what T is? You know that you want to match T = char but the compiler does not know that.
It could look for a constructor basic_string<T>(char const *), for example. Even if that exists, it still does not say what T should be.
The compiler doesn't iterate over all possible typenames it knows about and attempt basic_string<T> for each one, and then see if there is a matching constructor.
Similar example:
template<typename T>
struct Foo
{
Foo(T t) {}
};
int main()
{
Foo(0); // error, can't deduce Foo<int>
}
When I compile the following code with MSVC++, I get an error:
struct A
{
template<typename T>
void operator<<(T&& x)
{
}
};
void f()
{
}
int main()
{
A().operator<<( f ); // ok
A() << f; // error
return 0;
}
g++ and clang both compile this code fine.
AFAIK, 'ok' and 'error' lines do exactly the same thing, and type T is deduced to void(&)(). Or is it void() and rvalue references to function are allowed? If so, what is their meaning?
Is it ok to pass functions by reference like that? Is it MSVC++ bug that it fails to compile 'error' line? BTW, the error output:
no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)
could be 'void A::operator <<<void(void)>(T (__cdecl &&))'
with[ T=void (void) ]
Why void operator<<(T&& x)? void operator<<(T& x) serves the purpose.
Function can be called with x() inside overloaded function as below
struct A
{
template<typename T>
void operator<<(T& x)
{
x();
}
};
void f()
{
}
int main()
{
A().operator<<( f );
A() << f;
return 0;
}
So, answering my own question:
The code provided is valid and while rvalue references to functions are allowed (they act identical to lvalue references), here during template deduction T should become void(&)().
A bug in MSVC prevents my code from compiling.
UPDATE: The bug has been fixed in Visual Studio 2013 compiler
In this code, I want to pass the address of x.y as the template parameter typename Name::Type leValue.
#include <iostream>
using std::cout;
using std::endl;
struct X {
X() : y(123) {}
const int y;
};
template<typename Name, typename Name::Type leValue>
void print() { cout << *leValue << endl; }
struct Foo {
typedef int X::* Type;
};
int main() {
X x;
print<Foo, &x.y>(); // What is the right syntax here?
}
However, with gcc 4.7.2, I get the following errors:
source.cpp: In function 'int main()':
source.cpp:22:5: error: parse error in template argument list
source.cpp:22:22: error: no matching function for call to 'print()'
source.cpp:22:22: note: candidate is:
source.cpp:11:6: note: template void print()
source.cpp:11:6: note: template argument deduction/substitution failed:
source.cpp:22:22: error: template argument 2 is invalid
If I instead change the typedef to typedef int Type;, and the print call to print<Foo, 3>();, then it works. I tried several things by looking at the error messages, but could not get the syntax right. I have also searched here, and found some useful posts dealing with template classes, but none dealing with template functions. I tried using those answers but it did not help.
Could you please help me with this syntax, or explain to me what I should try doing next to fix this?
Is this close to what you're looking for?
#include <iostream>
using std::cout;
using std::endl;
struct X {
X() : y(123) {}
const int y;
};
template<typename Name, typename Type, Type Name::*Member>
void print(Type& obj) { cout << obj.*Member << endl; }
int main() {
X x;
print<X, const int, &X::y>(x);
}
The address of x.y is unknown in compile time. You can take a pointer to the member y as template argument, however, you have to pass the address of the object instance in run-time.
It works when you change it to an int because your allowed to pass const ints as template parameters. Templates will not let you pass values as arguments because they need to be resolved at compile time.
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