I am trying to use concepts to overload templated functions whose argument depends on the template parameter. Unfortunately this fails on gcc, reporting an ambiguous overload.
In my project I have code that compiles in gcc 9.3 and fails in gcc 12.2. But in trying to minimize the code, I've ended up with this, which fails on both gcc versions, but works in clang 15.0.0:
#include <type_traits>
#include <iostream>
struct A
{
using value_type = int;
};
struct B
{
using value_type = int;
};
template<typename Candidate>
concept something = requires {
typename Candidate::value_type;
};
template<typename Candidate>
concept something_specific = something<Candidate> && std::is_same_v<Candidate, A>;
template<something T>
void foo()
{
std::cout << "Something" << std::endl;
}
template<something_specific T>
void foo()
{
std::cout << "Something specific" << std::endl;
}
template<something T>
void bar(const typename T::value_type& s)
{
std::cout << "Something: " << s << std::endl;
}
template<something_specific T>
void bar(const typename T::value_type& s)
{
std::cout << "Something specific: " << s << std::endl;
}
int main()
{
foo<B>(); // works
foo<A>(); // works
bar<B>(1); // works
bar<A>(2); // ambiguous overload
return 0;
}
See godbolt, the error message is as expected:
<source>:48:11: error: call of overloaded 'bar<A>(int)' is ambiguous
48 | bar<A>(2); // ambiguous overload
| ~~~~~~^~~
<source>:32:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
32 | void bar(const typename T::value_type& s)
| ^~~
<source>:37:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
37 | void bar(const typename T::value_type& s)
| ^~~
I expected the something_specific overload to be chosen, because the concept that's restricting the template parameter is more specific. We can see this is true for foo already, otherwise it would be ambiguous as well.
Of course I can make it work with a requires clause, but I expected it to work as-is. So I'd like to understand whether this is just not supported or if it's a compiler issue. Is clang correct in compiling this code?
Why this code doesn't compile ?
#include <iostream>
#include <typeinfo>
template <typename ...Ts>
void f();
template <typename T>
void f() {
std::cout << typeid(T).name() << std::endl;
}
template <typename T, typename U, typename ...Ts>
void f() {
std::cout << typeid(T).name() << ", ";
f<U, Ts...>();
}
int main(int argc, char** argv)
{
f<int, float, char>();
}
MSVC compiler error:
error C2668: 'f': ambiguous call to overloaded function
Expected output:
int, float, char
Side question: would there be a more modern way to do the same thing ?
EDIT
I've found a way to accept zero template pack:
#include <typeinfo>
#include <iostream>
#include <type_traits>
template <typename ...Ts>
using is_empty_pack = std::enable_if_t<sizeof ...(Ts) == 0>;
template <typename ...Ts, typename = is_empty_pack<Ts...>>
void f() {}
template <typename T, typename ...Ts>
void f() {
std::cout << typeid(T).name();
if constexpr (sizeof ...(Ts) > 0) std::cout << ", "; else std::cout << std::endl;
f<Ts...>();
}
int main(int argc, char *argv[])
{
f<>();
f<int>();
f<int, float>();
}
Any other suggestion?
Compiling with g++ gives a pretty clear explanation of what's happening:
prog.cc: In function 'int main(int, char**)':
prog.cc:20:24: error: call of overloaded 'f<int, float, char>()' is ambiguous
20 | f<int, float, char>();
| ~~~~~~~~~~~~~~~~~~~^~
prog.cc:5:6: note: candidate: 'void f() [with Ts = {int, float, char}]'
5 | void f();
| ^
prog.cc:13:6: note: candidate: 'void f() [with T = int; U = float; Ts = {char}]'
13 | void f() {
You've provided three different templated functions f, two of which could match what you've written here.
EDIT: Maybe you thought the first one was a declaration and the other two are specializations, but that's not how templates work. Specialization means specializing the type or value of a particular template argument, not specializing the number of template arguments.
Deleting
template <typename ...Ts>
void f();
will make the program compile and run with the expected behavior.
i want to use recursive variadic templates with a base case with more than 2 types. The following example does not compile. What could be the problem?
My expectation was that f<int, int>(5) would call a case like:
[with T = int; Arg = int; Args = {int}]
but that seems to be no option for the compiler (g++ c++17) ;)
template<class T, typename Arg, typename... Args>
void f(T a) {
}
template<class T, typename Arg>
void f (T a) {
}
int main() {
f<int, int>(5);
return 0;
}
<source>: In function 'int main()':
<source>:11:18: error: call of overloaded 'f<int, int>(int)' is ambiguous
11 | f<int, int>(5);
| ^
<source>:2:6: note: candidate: 'void f(T) [with T = int; Arg = int; Args = {}]'
2 | void f(T a) {
| ^
<source>:6:6: note: candidate: 'void f(T) [with T = int; Arg = int]'
6 | void f (T a) {
| ^
Compiler returned: 1
You can combine both cases and use a constexpr if statement in the function template:
#include <iostream>
template<class T, typename Arg, typename... Args>
void f(T a) {
std::cout << sizeof...(Args) << '\n';
if constexpr (sizeof...(Args) > 0) {
f<T, Args...>(a);
}
}
int main() {
f<int, int>(5);
std::cout << "---\n";
f<int, int, double, float, int>(5);
}
Output:
0
---
3
2
1
0
I am writing a macro, which generates helper methods to call all same-named methods of all base classes of a current class, and skip the base classes, that do not have methods of that name.
(I am not posting the actual macro, because I don't want to hurt anybody, below is my test file with one macro-generated method)
I managed to have it working without preserving the return values of these method.
Now I want to save the values and return a list.
Below is a function, generated by my macro, it is supposed to call methods named "base_method" of all bases, with int and string as arguments.
I don't understand, why I am getting the error (below the code).
#include <type_traits>
#include <list>
#include <iostream>
namespace detail{
template <typename> struct sfinae_true : std::true_type{};
}
namespace detail{
template <typename T, typename A1, typename A2>
static auto test_base_method(int) ->
sfinae_true<decltype(std::declval<T>().base_method(std::declval<A1>(), std::declval<A2>()))>;
template <typename , typename A1, typename A2>
static auto test_base_method(long) ->
std::false_type;
template <typename T, typename A1, typename A2>
struct has_base_method : decltype(test_base_method<T, A1, A2>(0)){};
template <typename Base, typename T, std::enable_if_t<has_base_method<Base,int,std::string>::value, bool> = true >
auto call_base_method_if_any(T& obj, int arg1, std::string arg2) ->
decltype( obj.Base::base_method(std::declval<int>(), std::declval<std::string>()))
{
return obj.Base::base_method(arg1, arg2);
}
template <typename Base, typename T, std::enable_if_t<!has_base_method<Base,int,std::string>::value, bool> = false>
auto call_base_method_if_any(T&, int, std::string) -> bool
{
return false;
}
};
template <typename ... T>
class Z : public T ... {
public:
auto call_base_method_of_all_bases_if_any(int arg1, std::string arg2) -> std::list<bool> {
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
}
};
struct A{
bool base_method(int, bool){ std::cout << "A\n"; return true; }
bool base_method_narg(){ std::cout << "A no arg\n"; return true; }
};
struct B{ void base_method(int, bool){ std::cout << "B\n"; } };
struct C{ void base_method(int a, std::string b){ std::cout << "C, int = " << a << ", string = " << b; } };
struct D{ };
int main(){
Z<A> b;
Z<A,B> c;
Z<A,B,C> d;
Z<A,B,C,D> a;
std::cout << "a:" << std::endl;
auto x =a.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "b:" << std::endl;
b.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "c:" << std::endl;
c.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "d:" << std::endl;
d.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
}
Compiling errors:
g++ --std=c++14 expression_sfinae.3.cpp
expression_sfinae.3.cpp: In instantiation of ‘std::__cxx11::list<bool> Z<T>::call_base_method_of_all_bases_if_any(int, std::__cxx11::string) [with T = {A, B, C, D}; std::__cxx11::string = std::__cxx11::basic_string<char>]’:
expression_sfinae.3.cpp:48:63: required from here
expression_sfinae.3.cpp:27:99: error: no matching function for call to ‘std::__cxx11::list<bool>::list(<brace-enclosed initializer list>)’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: candidate: template<class _InputIterator, class> std::__cxx11::list<_Tp, _Alloc>::list(_InputIterator, _InputIterator, const allocator_type&)
list(_InputIterator __first, _InputIterator __last,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed:
expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
list(initializer_list<value_type> __l,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
list(list&& __x) noexcept
^
u/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed:
expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
list(initializer_list<value_type> __l,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
list(list&& __x) noexcept
^
// 9000 lines ommited
P.S. I also did not yet figured out how to deal with functions, that do not return anything (I don't think you can have a list of void). Any advices are welcome.
The root cause of the problem is that C's method returns void:
struct C {
void base_method(int a, std::string b);
^^^^^
};
And you're trying to use that return type to build up your std::list<bool>.
Going on the assumption that you'd want to exclude this case, I'd rewrite your details like so:
// this is the preferred overload (last argument is 'int' instead of '...')
// but will be SFINAE-d out if base_method() isn't callable with int or string
// or it returns something other than bool
template <class Base, class T,
class = std::enable_if_t<std::is_same<bool,
decltype(std::declval<Base&>().base_method(
std::declval<int>(),
std::declval<std::string>()))
>::value>>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, int)
{
return obj.Base::base_method(arg1, arg2);
}
// fallback overload
template <class Base, class T>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, ...) {
return false;
}
template <class Base, class T>
bool call_base_method_if_any(T& obj, int arg1, std::string arg2) {
return call_base_method_if_any_impl<Base>(obj, arg1, arg2, 0);
}
I am trying to create a (very) simple messaging system, but am stuck with C++03. I have solved this problem before using C++11 features, but I do not have such luxuries anymore.
The target compiler is Visual Studio 2008's (which I think is VC9?), but I do not have it with me at this time; this said I can reproduce the problem by simply forcing g++ to the C++03 standard.
I have managed to isolate the problem in the following piece of code:
testing03.cpp
#include <iostream>
#include <map>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/units/detail/utility.hpp>
struct BaseMessage {};
struct DerivedMessage : BaseMessage {};
std::map<std::string, boost::function<void(BaseMessage)>> subscribers;
template <typename Type>
void ask(boost::function<void(Type)> function)
{
std::cout << "Asking for " << boost::units::detail::demangle(typeid(Type).name()) << std::endl;
subscribers[boost::units::detail::demangle(typeid(Type).name())] = function;
}
void testBase(BaseMessage)
{
std::cout << "In testBase" << std::endl;
}
void testDerived(DerivedMessage)
{
std::cout << "In testDerived" << std::endl;
}
int main()
{
ask<BaseMessage>(boost::bind(testBase, _1));
ask<DerivedMessage>(boost::bind(testDerived, _1));
}
...and there could be any number of derived messages.
The error that stands out to me the most
no known conversion for argument 1 from ‘BaseMessage’ to ‘DerivedMessage’
Full output of running g++ -std=c++03 testing03.cpp
In instantiation of ‘static void boost::detail::function::void_function_obj_invoker1<FunctionObj, R, T0>::invoke(boost::detail::function::function_buffer&, T0) [with FunctionObj = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage]’:
/usr/include/boost/function/function_template.hpp:934:38: required from ‘void boost::function1<R, T1>::assign_to(Functor) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage]’
/usr/include/boost/function/function_template.hpp:722:7: required from ‘boost::function1<R, T1>::function1(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1069:16: required from ‘boost::function<R(T0)>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1124:5: required from ‘typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0)>&>::type boost::function<R(T0)>::operator=(Functor) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0)>&>::type = boost::function<void(BaseMessage)>&]’
testing03.cpp:18:67: required from ‘void ask(boost::function<void(Type)>) [with Type = DerivedMessage]’
testing03.cpp:34:50: required from here
/usr/include/boost/function/function_template.hpp:153:11: error: no match for call to ‘(boost::function<void(DerivedMessage)>) (BaseMessage&)’
BOOST_FUNCTION_RETURN((*f)(BOOST_FUNCTION_ARGS));
^
/usr/include/boost/function/function_template.hpp:1048:7: note: candidate is:
class function<BOOST_FUNCTION_PARTIAL_SPEC>
^
/usr/include/boost/function/function_template.hpp:761:17: note: boost::function1<R, T1>::result_type boost::function1<R, T1>::operator()(T0) const [with R = void; T0 = DerivedMessage; boost::function1<R, T1>::result_type = void]
result_type operator()(BOOST_FUNCTION_PARMS) const
^
/usr/include/boost/function/function_template.hpp:761:17: note: no known conversion for argument 1 from ‘BaseMessage’ to ‘DerivedMessage’
To be clear on what I'm asking, how can I change my ask function (which is where I believe the problem is), so that I can call functions with types derived from a common base using C++03?
You can use a helper function that performs the cast:
#include <map>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/units/detail/utility.hpp>
#include <boost/smart_ptr.hpp>
struct BaseMessage {};
struct DerivedMessage : BaseMessage {};
// we need a function to perform the `static_cast` from the base class
// to the derived class
template<class T, class F>
void invoke_binder(F const& f, BaseMessage const& m)
{
f( static_cast<T const&>(m) );
}
std::map<std::string, boost::function<void(BaseMessage const&)> > subscribers;
template <typename Type, class F>
void ask(F const& f)
{
std::cout << "Asking for "
<< boost::units::detail::demangle(typeid(Type).name())
<< std::endl;
// boost::function is a polymorphic function wrapper;
// it can store any Callable
// Here, we store the actual function to be called, `f`, inside the
// function object returned from `bind`.
// The `bind` expression returns a function object that invokes
// `invoke_binder` which invokes the bound function `f`.
subscribers[boost::units::detail::demangle(typeid(Type).name())]
= boost::bind(&invoke_binder<Type, F>, f, _1);
}
Note the change in the signature of the callbacks; also, the calls to ask now directly use the function pointer. There's some error if it's a bind expression -- and I don't know what causes it.
void testBase(BaseMessage const&)
{
std::cout << "In testBase" << std::endl;
}
void testDerived(DerivedMessage const&)
{
std::cout << "In testDerived" << std::endl;
}
template<class T>
void call(T const& p)
{
subscribers_t::const_iterator i =
subscribers.find( boost::units::detail::demangle(typeid(T).name()) );
if(i != subscribers.end())
{
(i->second)(p);
}else
{
// error handling
}
}
int main()
{
ask<BaseMessage>(&testBase);
ask<DerivedMessage>(&testDerived);
DerivedMessage d;
call(d);
BaseMessage b;
call(b);
}
A bit more complicated, but w/o bind expressions, is the following:
template<class Type, class F>
struct wrapper
{
F f;
wrapper(F const& f) : f(f) {}
void operator()(BaseMessage const& p)
{
return f( static_cast<Type const&>(p) );
}
};
template <typename Type, class F>
void ask(F const& f)
{
std::cout << "Asking for "
<< boost::units::detail::demangle(typeid(Type).name())
<< std::endl;
subscribers[boost::units::detail::demangle(typeid(Type).name())]
= wrapper<Type, F>(f);
}
And strangely enough, this works even when called with ask<BaseMessage>(boost::bind(&testBase, _1));. I suspect a wrapped binder is treated in a special way that causes the error in the first version when called like this.