I'm having a problem with a part of a larger program where something that I'd say is not ambiguous is considered ambiguous by both g++ and clang++
#include <functional>
#include <string>
struct Foo {
Foo(int) {}
Foo(std::string) {}
operator int () const { return 42; }
operator std::string () const { return ""; }
void foo(std::function<void(Foo&, int)>f);
void foo(std::function<void(const Foo&, int)>f) const; // xxx
void foo(std::function<void(const std::string&, Foo&)>f);
void foo(std::function<void(const std::string&, const Foo&)>f) const;
void bar() const {
this->foo([](const Foo&, int){}); // xxx
}
};
I'd expect the invocation of ::foo made in bar to be unambiguously resolved to the const version marked with xxx, while instead both compilers complain that the overload resolution is ambiguous:
g++ -std=c++11 -c -Wall amb.cpp
amb.cpp: In member function ‘void Foo::bar() const’:
amb.cpp:18:40: error: call of overloaded ‘foo(Foo::bar() const::<lambda(const Foo&, int)>)’ is ambiguous
this->foo([](const Foo&, int){});
^
amb.cpp:12:10: note: candidate: void Foo::foo(std::function<void(const Foo&, int)>) const
void foo(std::function<void(const Foo&, int)>f) const;
^
amb.cpp:15:10: note: candidate: void Foo::foo(std::function<void(const std::basic_string<char>&, const Foo&)>) const
void foo(std::function<void(const std::string&, const Foo&)>f) const;
^
Why it's not clear which version I want to call? How can I work around this problem?
The problem is that some type information is lost when passing from a lambda to an std::function object (in particular the argument types).
To be more specific for example a function accepting std::function<void(int)> and another accepting std::function<void(double)> are considered equally good overloads for a void lambda accepting an int argument.
For example with
#include <functional>
void foo(std::function<void(int)>f);
void foo(std::function<void(double)>f);
void bar() {
foo([](int){});
}
the call in bar is considered ambiguous
amb2.cpp: In function ‘void bar()’:
amb2.cpp:8:18: error: call of overloaded ‘foo(bar()::<lambda(int)>)’ is ambiguous
foo([](int){});
^
amb2.cpp:4:6: note: candidate: void foo(std::function<void(int)>)
void foo(std::function<void(int)>f);
^
amb2.cpp:5:6: note: candidate: void foo(std::function<void(double)>)
void foo(std::function<void(double)>f);
^
Workaround
A solution is to explicitly create the proper std::function object with:
this->foo(std::function<void(const Foo&, int)>([](const Foo&, int){}));
You could make the constructor taking the std::string argument explicit. That way the compiler can't make an implicit conversion from std::string to Foo.
Related
Is there any way to construct this code so that I do not have to specify the template parameter of bar?
i.e. it does not currently compile, but will compile if I change bar(v) to bar<int>(v) in main.
#include <cstdlib>
#include <vector>
template <class T>
struct foo
{
foo(std::vector<T>& v)
{
}
};
template <class T>
void bar(const foo<T>& f)
{
}
int main()
{
std::vector<int> v;
bar(v); // does not compile
bar<int>(v); // compiles but is undesirable
return EXIT_SUCCESS;
}
The compiler is only allowed one implicit conversion but you can add an overload for bar that tries to convert whatever you give it to a foo and then calls your original bar with the temporary foo (which is what would happen if two implicit conversions was allowed in your original code):
template<typename T>
void bar(T& v) {
return bar(foo(v));
}
Since the conversion constructor in foo doesn't take the argument by const&, neither can this bar overload.
I am trying to make an HTTP class, and I want to use C++11 (not C++14 yet) callbacks via lambdas. I have 2 mockups available, the first one works... but looks ugly. The second one I am aiming, is not compiling (error at the end).
I cannot use std::function, as this is an embedded project, and that template generates a lot of code.
#include <cstring>
class HTTP
{
public:
void get1(const char* url, void* context, void (*callback)(void*, const char*) )
{
callback(context, "");
}
void get2(const char* url, void (*callback)(const char*) )
{
callback("");
}
};
void test()
{
int k;
HTTP http;
http.get1( "http://google.com", &k, [](void* context, const char* s){
int *k = (int*) context;
*k = strlen(s);
});
// this does not compile, looking for other alternatives
http.get2( "http://google.com", [&k](const char* s){
k = strlen(s);
});
}
Error from gcc (xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0)
HttpRequests.cpp: In function 'void test()':
HttpRequests.cpp:29:6: error: no matching function for call to 'HTTP::get2(const char [18], test()::<lambda(const char*)>)'
});
^
HttpRequests.cpp:11:10: note: candidate: void HTTP::get2(const char*, void (*)(const char*))
void get2(const char* url, void (*callback)(const char*) )
^
HttpRequests.cpp:11:10: note: no known conversion for argument 2 from 'test()::<lambda(const char*)>' to 'void (*)(const char*)'
Lambdas without a capture list are compatible with function pointers, so your first lambda can be passed as an argument to get1(). However, lambdas with a capture list are not convertible to function pointers so it can not be passed to get2().
Lambdas with captures have state but functions can not have state, which is why such lambdas are not convertible to function pointers.
The most common way to have a function accept any lambda (or any callable object) is to use function templates:
class HTTP {
// ...
template <typename Callable>
void get1(const char* url, void* context, Callable callback)
{
callback(context, "");
}
template <typename Callable>
void get2(const char* url, Callable callback)
{
callback("");
}
}
Being function templates, code size might become an issue. If that's not acceptable, then keep your current functions and restrict yourself to never passing lambdas that use captures.
I'm trying to write a container that is able to categories objects and store pointers for which a categorisation function is true.
My problem is, that it does not compile and since I'm inexperienced in regard to callables like std::function or lambdas, I'm not sure how to fix it.
I want such a container, since I've the need to get some "Categories" often - and this makes it easy to cache the results. Especially if, in this example, the Dogs change their sound, the Categories can simply be recreated (since the callable is still there).
What is more, I'm interested in a solution that delivers good performance. As I read, std::functions are unlikely to be inlined. Is there a way to deliver inlined-performance?
The compiler says:
main.cpp: In function 'int main()':
main.cpp:51:108: error: no matching function for call to 'CategoryContainer<Dog>::addCategory(CategoryContainer<Dog>::Categories, main()::<lambda(auto:1&)>)'
dogs.addCategory( CategoryContainer<Dog>::Categories::Wuff, [](auto& d){return d.makeSound()=="Wuff";} );
main.cpp:39:10: note: candidate: void CategoryContainer<T>::addCategory(CategoryContainer<T>::Categories, std::function<bool()>) [with T = Dog]
void addCategory(Categories cat, std::function<bool()> f) {
main.cpp:39:10: note: no known conversion for argument 2 from 'main()::<lambda(auto:1&)>' to 'std::function<bool()>'
main.cpp: In lambda function:
main.cpp:52:71: error: expected '{' before '(' token
dogs.addCategory( CategoryContainer<Dog>::Categories::WauWau, []()(auto& d){return d.makeSound()=="WauWau";} );
main.cpp: In function 'int main()':
main.cpp:52:72: error: expected primary-expression before 'auto'
dogs.addCategory( CategoryContainer<Dog>::Categories::WauWau, []()(auto& d){return d.makeSound()=="WauWau";} );
And here is my code:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
class Dog
{
public:
std::string makeSound() { return _sound; }
void setSound(std::string sound) { _sound=sound; }
private:
std::string _sound = "Wuff";
};
template<class T>
class CategoryContainer
{
public:
using objectContainer = std::vector<T>;
using pointerContainer = std::vector<T*>;
enum class Categories { Wuff, WauWau }; // Dogs are e.g. destinguished by the sound they make.
struct Category {
std::function<bool()> func;
pointerContainer pointers;
Category(std::function<bool()> f, objectContainer& data) : func(f) {
for(auto& i : data)
if( func(i) )
pointers.emplace_back(&i);
}
};
CategoryContainer(size_t n) {
data.resize(n); // Construct so many dogs.
}
void addCategory(Categories cat, std::function<bool()> f) {
indexed[cat] = Category(f, data);
}
private:
objectContainer data;
std::unordered_map<Categories, Category> indexed;
};
int main()
{
CategoryContainer<Dog> dogs(10);
dogs.addCategory( CategoryContainer<Dog>::Categories::Wuff, [](auto& d){return d.makeSound()=="Wuff";} );
dogs.addCategory( CategoryContainer<Dog>::Categories::WauWau, []()(auto& d){return d.makeSound()=="WauWau";} );
}
You pass [](auto& d){return d.makeSound()=="Wuff";} as a functor but addCategory is declared as
void addCategory(Categories cat, std::function<bool()> f) {
So the functor is expected to take zero arguments but actually takes Instead of one.std::function<bool()> you should use std::function<bool(const T&)>
As pointed by Alexey Guseynov (+1), your func() in Category receive a T object.
So, as suggested, should be std::function<bool(T const &)>.
And you have to correct this in three points:
1) std::function<bool()> func; become std::function<bool(T const &)> func;
2) Category(std::function<bool()> f, objectContainer& data) : func(f) become Category(std::function<bool(T const &)> f, objectContainer& data) : func(f)
3) void addCategory(Categories cat, std::function<bool()> f) become void addCategory(Categories cat, std::function<bool(T const &)> f).
But isn't enough.
Now the makeSound() method of Dog is used with const Dog instantiations inside your lambda funtions. So makeSound() (that doesn't modify the object) should be modified in a const method
std::string makeSound() const { return _sound; }
At this point I have some errors because Dogs is incompatible with std::unordered_map because (if I understand correctly) there isn't a specialization for std::hash<CategoryContainer<Dog>::Categories>
But, to avoid this problem, if you can change the
std::unordered_map<Categories, Category> indexed;
in a ordered std::map (adding #include <map> too)
std::map<Categories, Category> indexed;
and if you change, in addCategory(), the row
indexed[cat] = Category(f, data);
that give error for reasons (involving constructors) that I don't want investigate further, with
indexed.emplace(std::piecewise_construct,
std::forward_as_tuple(cat),
std::forward_as_tuple(f, data));
you shoul'd be able to compile your example.
The minimal code below gives me a compile error:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
{
}
int main(int argc, char *argv[])
{
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
return 0;
}
The error is:
foo.cc: In function ‘int main(int, char**)’:
foo.cc:13:69: error: no matching function for call to ‘print_action(std::function<std::basic_string<char>*(unsigned char, std::basic_string<char>)>)’
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
^
foo.cc:13:69: note: candidate is:
foo.cc:7:6: note: template<class ActionType, class ... Cols> void print_action(std::function<ActionType*(Cols ..., ActionType)>)
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
^
foo.cc:7:6: note: template argument deduction/substitution failed:
foo.cc:13:69: note: mismatched types ‘std::basic_string<char>’ and ‘unsigned char’
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
^
foo.cc:13:69: note: ‘std::function<std::basic_string<char>*(unsigned char, std::basic_string<char>)>’ is not derived from ‘std::function<std::basic_string<char>*(Cols ..., std::basic_string<char>)>’
I also try to change the input parameter to a simple pointer function by below code:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
void print_action(ActionType*(*action_factory)(Cols..., ActionType))
{
}
string* foo_factory(uint8_t cols, string act)
{
}
int main(int argc, char *argv[])
{
print_action<string, uint8_t>(foo_factory);
return 0;
}
It gives me the same error. After some works my last guess is that it is a bug of g++ because if I change the variadic template parameter to a simple parameter no errors happen.
Am I right or I missed some syntax of c++?
I use g++-4.8.4 with c++11 flag(I checked it using clang-3.4 and g++-4.9.2).
EDIT:
If I change the code to this:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
struct Foo
{
Foo()
{}
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
{
}
};
int main(int argc, char *argv[])
{
Foo<string, uint8_t> f;
f.print_action(function<string*(uint8_t, string)>());
return 0;
}
I get no error. I don`t understand this behavior because in both situations I defined the template parameters explicitly and I did not expect any deduction, but It seems that compiler does some deduction when it is a template function but not when it is a member function of a template class.
The issue is that you have (Cols..., ActionType). One might think that the compiler should notice that Cols... should be all the arguments before the end so long as the end is the same as ActionType, but this is not how the language works.
A simple solution would to just deduce the entire argument list. Compilation will fail anyway if you happen to use the final argument as in a way the type doesn't support, and you could always add in a static_assert to ensure that the final parameter is the same as ActionType if you really wanted.
template<typename ActionType, typename... Cols>
void print_action(function<ActionType*(Cols...)> action_factory)
{
//Maybe with a static_assert like this
using LastArg = typename std::tuple_element<
sizeof...(Cols)-1, //number of args - 1
std::tuple<Cols...>
>::type;
static_assert(std::is_same<LastArg, ActionType>::value,
"The final argument must be the same type as ActionType");
}
Live Demo
Usually variadic templates are written thus
template<typename First, typename... Rest> class test;
The compiler matches the first argument and leaves the rest (empty or more) to variadic part. The behaviour when reversed is not as expected. Variadic template arguments are greedy, in that, all arguments are eaten-up by it, leaving none to the last.
Your example code compiles fine, when the order is reversed:
template<typename ActionType, typename... Cols>
void print_action(function<ActionType*(ActionType, Cols...)>) {
}
int main()
{
print_action(function<string*(string, uint8_t)>());
}
Live example.
There's a difference between argument type deduction and instantiation. Quote from C++ Templates: The Complete Guide:
The process of replacing template parameters by concrete types is called instantiation. It results in an instance of a template.
When a function template is instantiated, we get a function out of it; same for class/struct.
It seems that compiler does some deduction when it is a template function but not when it is a member function of a template class.
There is no type deduction or instantiation happening for the function call. It is not a function template, but just a function. The call is just another ordinary function call.
However, the struct is really a struct template and a struct is created out of the template, when an object was created. For this struct template instantiation
template<typename ActionType, typename... Cols>
struct Foo;
the order is correct (the variadic argument is the last) and so it works.
Consider this simple code snippet:
static void Foo(std::string&& arg) {
printf("(universal reference) = %s\n", arg.c_str());
}
static void Foo(const std::string&& arg) {
printf("(const universal reference) = %s\n", arg.c_str());
}
static void Foo(std::string& arg) {
printf("(reference) = %s\n", arg.c_str());
}
static void Foo(const std::string& arg) {
printf("(const reference) = %s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value);
Foo(const_value);
Foo(std::string("temporary"));
Foo("litteral");
}
The resulting output under Clang is:
(reference) = value
(const reference) = const_value
(universal reference) = temporary
(universal reference) = literal
Why is the value case not using the universal reference version of the function? I thought one of the key benefits of universal references was that they could accept both lvalues and rvalues?
PS: It's not possible to force it either:
static void Foo(std::string&& arg) {
printf("(universal reference) = %s\n", arg.c_str());
}
//static void Foo(const std::string&& arg) {
// printf("(const universal reference) = %s\n", arg.c_str());
//}
//
//static void Foo(std::string& arg) {
// printf("(reference) = %s\n", arg.c_str());
//}
//
//static void Foo(const std::string& arg) {
// printf("(const reference) = %s\n", arg.c_str());
//}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value); <--- FAILS COMPILING: No matching function for call to 'Foo'
// Foo(const_value);
Foo(std::string("temporary"));
Foo("literal");
}
UPDATE: It appears "universal references" are only for templates, not regular functions, which explains why the above is not working as I was expecting.
However here's a version using a templated function:
template<typename T>
static void Foo(T&& arg) {
printf("%s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo<std::string>(value); <--- FAILS COMPILING: No matching function for call to 'Foo'
Foo<std::string>(const_value); <--- FAILS COMPILING: No matching function for call to 'Foo'
Foo<std::string>(std::string("temporary"));
Foo<std::string>("literal");
}
Why is the value case still not working through universal references (I understand why the const_value case is not)?
UPDATE: For reference, here's the final version that works with both lvalues and rvalues:
template<typename T>
static void Foo(T&& arg) {
printf("%s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value);
Foo(const_value);
Foo(std::string("temporary"));
//Foo("literal"); <--- Cannot work anyway since template is instantiated with arg as a const char*
}
Those are not "universal references". Those are merely rvalue references.
A "universal reference" (FYI: this term has fallen out of disfavor) refers explicitly to an rvalue reference applied directly to a fully template deduced type (as opposed to something like vector<T> &&v, which is only partially deduced) when using template argument deduction. None of your functions are template functions, so they behave like regular rvalue references.
foo(value) calls the lvalue reference version because variables passed as parameters bind to lvalue reference parameter types.
However here's a version using a templated function:
None of those functions use template argument deduction. You're explicitly specifying the template parameter directly, not allowing it to be deduced by the arguments you call it with.
Or more to the point:
Foo<std::string>(value);
Foo(value);
Foo<std::string>(std::string("temporary"));
Foo(std::string("temporary"));
In the first case, T is std::string, because you specified it to be so. Therefore, Foo<std::string> takes a std::string&&. value cannot bind to an rvalue reference, so it's a compile error.
In the second case, T is deduced through template argument deduction based on the argument expression. value is an lvalue, so T is deduced as std::string&. Therefore, Foo in this case takes a std::string& &&, which devolves down to std::string&.
In the third case, T is std::string. Therefore again, Foo<std::string> takes a std::string&&. A temporary can bind to an rvalue reference just fine, so it does so.
In the fourth case, T is deduced through template argument deduction based on the argument expression. The expression is a std::string temporary prvalue, so T is deduced as std::string (note the lack of references). So Foo in this case takes a std::string&&.