I have a simple program that I am attempting to use to test C++17's class template argument deduction.
#include <iostream>
#include <list>
int main(int argc, const char * argv[]) {
const char* a = "Hello";
std::list x(1, a);
return 0;
}
I would like to std::list to deduce the list having type const char*. However when attempting to run this code I obtain the error No viable constructor or deduction guide for deduction of template arguments of 'list'. Specifically the constructor that should be matched to this list(size_type __n, const value_type& __x); reports an error saying:
Candidate template ignored: substitution failure [with _Tp = const char *, _Alloc = std::__1::allocator<const char *>]: 'size_type' is a protected member of 'std::__1::__list_imp<const char *, std::__1::allocator<const char *> >'
I am curious why this does not work and yet a program like this is completely well formed with std::pair able to easily deduce the arguments:
#include <iostream>
#include <list>
int main(int argc, const char * argv[]) {
const char* a = "Hello";
std::pair x(1, a);
return 0;
}
Thank you.
clang 5 and 6 and gcc 7 and 8 compile your code without problem. So you are using either a compiler that didn't implement correctly the deduction guides or a library that doesn't have the appropriate deduction guides for std::list
Related
I'm trying to map string to function pointer, so that I can call the function with iter->second(arg) instead of if-else.
I have written a simple one without class, and it works as expected.
But when I modify it as below, it shows compile errors.
#include <functional>
#include <iostream>
#include <unordered_map>
#include <string>
using std::string;
class A{
private:
int a(int num, string s) { return s.size() + num; }
int b(int num, string s) { return num - s.size(); }
public:
void ido(string str){
typedef std::function<int(int, string)> process_func;
std::unordered_map<string, process_func> m;
m.insert(std::make_pair<string, process_func>("a", &A::a));
// using std::placeholders::_1;
// m.insert(std::make_pair<string, process_func>("a", std::bind(&A::a, this, _1)));
// m["a"] = std::bind(&A::a, this, _1);
// m.insert({{"a", &A::a}, {"b", &A::b}});
auto x = m.find(str);
if(x == m.end()) {
std::cout << "Not supported!" << std::endl;
}
std::cout << x->second(10, "hello") << std::endl;
}
};
int main(int argc, char* argv[]) {
A a;
a.ido(string(argv[1]));
return 0;
}
The errors are:
function.cc: In member function ‘void A::ido(std::string)’:
function.cc:17:65: error: no matching function for call to ‘make_pair(const char [2], int (A::*)(int, std::string))’
m.insert(std::make_pair<string, process_func>("a", &A::a));
^
function.cc:17:65: note: candidate is:
In file included from /usr/include/c++/4.8.2/utility:70:0,
from /usr/include/c++/4.8.2/tuple:38,
from /usr/include/c++/4.8.2/functional:55,
from function.cc:1:
/usr/include/c++/4.8.2/bits/stl_pair.h:276:5: note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
make_pair(_T1&& __x, _T2&& __y)
^
/usr/include/c++/4.8.2/bits/stl_pair.h:276:5: note: template argument deduction/substitution failed:
function.cc:17:65: note: cannot convert ‘&A::a’ (type ‘int (A::*)(int, std::string) {aka int (A::*)(int, std::basic_string<char>)}’) to type ‘std::function<int(int, std::basic_string<char>)>&&’
m.insert(std::make_pair<string, process_func>("a", &A::a));
What does the error mean? How to fix it?
While your functions 'a' and 'b' do not depend on 'this' (they do not access anything inside class A), the compiler is not smart enough to deduce this. So the error means that you are trying to convert 'pointer to method' to 'pointer to function', which is incorrect conversion. 'Pointer to method' requires and object to be called on. You need to declare methods 'a' and 'b' as 'static' to indicate that they are actually standalone functions, not methods of the class.
Specifically, I'm wondering why this compiles:
#include <memory>
#include "make_unique.hpp"
void foo(const char *s){
std::unique_ptr<const char*>ptr = std::make_unique<const char*>(s);
}
and this does not:
#include <memory>
#include "make_unique.hpp"
void foo(const char *s){
std::unique_ptr<const char*>ptr(s);
}
Seems to be working when I write it like in make_unique implementation:
#include <memory>
#include "make_unique.hpp"
#include <iostream>
std::unique_ptr<const char*> foo(const char *s){
/* return std::make_unique<const char*>(s); */
return std::unique_ptr<const char*>(new decltype(s)(std::forward<decltype(s)>(s)));
}
int main(){
const char * s = "bar";
std::unique_ptr<const char*>ptr = foo(s);
std::cout<<*ptr;
}
EDIT* errors(truncated)
error: no matching conversion for functional-style cast from 'const char *' to 'std::unique_ptr<const char *>'
return std::unique_ptr<const char*>(s);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/unique_ptr.h:169:7: note: candidate constructor not viable: no known
conversion from 'const char *' to 'pointer' (aka 'const char **') for 1st argument; take the address of the argument with &
unique_ptr(pointer __p) noexcept
^
std::make_unique<T>(args...) expects to construct a T from args....
std::unique_ptr<T>(p) expects p to be an owning value of type T *.
Take note that your unique pointer models a const char **, not a const char *. So in (1) you create a new, dynamically allocated char pointer whose value is copied from s, whereas in (2) you're trying to create a unique pointer that takes ownership from something, but you're passing it a constructor argument of the wrong type.
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>
}
Is it possible in C++11 to overload const char*'s and string literals (const char[])?
The idea is to avoid having to call strlen to find the string length when this length is known already.
This snippet breaks on G++ 4.8 and Clang++ 3.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
template<typename T, int N>
void length(const T(&data)[N]) {
printf("%u[]\n", N - 1);
}
template<typename T>
void length(const T* data) {
printf("*%u\n", (unsigned)strlen(data));
}
int main() {
length("hello");
const char* p = "hello";
length(p);
return 0;
}
Error (Clang):
test2.cpp:16:3: error: call to 'length' is ambiguous
length("hello");
^~~~~~
test2.cpp:6:6: note: candidate function [with T = char, N = 6]
void length(const T(&data)[N]) {
^
test2.cpp:11:6: note: candidate function [with T = char]
void length(const T* data) {
^
1 error generated.
Hacked a bit, and this appears to work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
template<typename T, int N>
void length(const T(&data)[N]) {
printf("%u[]\n", N - 1);
}
template<typename T>
void length(T&& data) {
printf("*%u\n", (unsigned)strlen(data));
}
const char *foo() {
return "bar";
}
int main() {
length("hello");
const char* p = "hello";
length(p);
length(foo());
return 0;
}
Is this valid C++11? The string literal appears to overload on T&& when the array specialization is removed. What causes this ambigousness to be resolved, but not the one in the first code snippet?
In the first case, during overload resolution you have a perfect match requiring no conversion against an array to pointer conversion (which is in the category "lvalue transformation", along with lvalue to rvalue and function to pointer conversion). A difference that is only made by an lvalue transformation is not sufficient for overload resolution to pick a winner.
In the second case, during overload resolution, both functions have the exact same parameter type. Then partial ordering as the last resort finds that the second template would accept all arguments you ever pass to it, wheras the first template only accepts arrays. Therefor the first template in the second case is found more specialized and taken.
As for your other question - no, overloading specifically for string literals is not possible. You are always going to catch arrays of the same size along with them.
I intend to use shared_ptr quite a bit in an upcoming project, so (not being aware of std::make_shared) I wanted to write a variadic template function spnew<T>(...) as a shared_ptr-returning stand-in for new. Everything went smoothly till I attempted to make use of a type whose constructor includes an initializer_list. I get the following from GCC 4.5.2 when I try to compile the minimal example below:
In function 'int main(int, char**)':
too many arguments to function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]'
In function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]':
no matching function for call to 'Example::Example()'
Oddly enough, I get equivalent errors if I substitute std::make_shared for spnew. In either case, it seems to be incorrectly deducing the parameters when an initializer_list is involved, erroneously treating Args... as empty. Here's the example:
#include <memory>
#include <string>
#include <vector>
struct Example {
// This constructor plays nice.
Example(const char* t, const char* c) :
title(t), contents(1, c) {}
// This one does not.
Example(const char* t, std::initializer_list<const char*> c) :
title(t), contents(c.begin(), c.end()) {}
std::string title;
std::vector<std::string> contents;
};
// This ought to be trivial.
template<class T, class... Args>
std::shared_ptr<T> spnew(Args... args) {
return std::shared_ptr<T>(new T(args...));
}
// And here are the test cases, which don't interfere with one another.
int main(int argc, char** argv) {
auto succeeds = spnew<Example>("foo", "bar");
auto fails = spnew<Example>("foo", {"bar"});
}
Is this just an oversight on my part, or a bug?
You could do this -
#include <memory>
#include <string>
#include <iostream>
#include <vector>
struct Example {
template<class... Args>
Example(const char* t, Args... tail) : title(t)
{
Build(tail...);
}
template<class T, class... Args>
void Build(T head, Args... tail)
{
contents.push_back(std::string(head));
Build(tail...);
}
template<class T>
void Build(T head)
{
contents.push_back(std::string(head));
}
void Build() {}
std::string title;
std::vector<std::string> contents;
};
template<class T, class... Args>
std::shared_ptr<T> spnew(Args... args) {
return std::shared_ptr<T>(new T(args...));
}
int main(int argc, char** argv) {
auto succeeds = spnew<Example>("foo", "bar");
auto fails = spnew<Example>("foo", "bar", "poo", "doo");
std::cout << "succeeds->contents contains..." << std::endl;
for ( auto s : succeeds->contents ) std::cout << s << std::endl;
std::cout << std::endl << "fails->contents contains..." << std::endl;
for ( auto s : fails->contents ) std::cout << s << std::endl;
}
This, despite the generic templates is type safe as the compiler will complain about
the contents.push_back if the passed type is not convertible to a const char *.
As described above, your code was working fine with gcc 4.6 however the warning you get is explained here
why-doesnt-my-template-accept-an-initializer-list, and is possibly not standards
compliant, although the c++0x standard is yet to be published so this could change.
With gcc-4.7 (probably would work on gcc-4.6 too, just branched) with warnings:
foo.cpp: In function ‘int main(int, char**)’:
foo.cpp:29:47: warning: deducing ‘Args ...’ as ‘std::initializer_list<const
char*>’ [enabled by default]
foo.cpp:22:20: warning: in call to ‘std::shared_ptr<_Tp1> spnew(Args ...)
[with T = Example, Args = {const char*, std::initializer_list<const
char*>}]’ [enabled by default]
foo.cpp:29:47: warning: (you can disable this with -fno-deduce-init-list)
[enabled by default]
I'm not sure why anyone would want to beef about init-list deduction though.
There is a related thread:
Why doesn't my template accept an initializer list
Basically, a bare init-list doesn't have a type.