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?
I am trying to build an operator << overload to print all the standard library container types. So far, the overload works well, but when I use it in a generic program, it breaks when I try to print a simple std::string. In fact, this program:
#include <iostream>
#include <utility>
#include <vector>
#include <map>
#include <string>
// Helper Function to Print Test Containers (Vector and Map)
template <typename T, typename U>
inline std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) {
out << "[" << p.first << ", " << p.second << "]";
return out;
}
template <template <typename, typename...> class ContainerType, typename
ValueType, typename... Args>
std::ostream& operator <<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
for (const auto& v : c) {
os << v << ' ';
}
return os;
}
int main()
{
std::vector <int> v = { 1,2,3 };
std::cout << v;
std::map <int,int> m = { { 1, 1} , { 2, 2 }, { 3, 3 } };
std::cout << m;
std::string s = "Test";
std::cout << s;
}
Gives me this error:
prove.cpp: In function ‘int main()’:
prove.cpp:32:13: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’})
32 | std::cout << s;
| ~~~~~~~~~ ^~ ~
| | |
| | std::string {aka std::__cxx11::basic_string<char>}
| std::ostream {aka std::basic_ostream<char>}
prove.cpp:16:15: note: candidate: ‘std::ostream& operator<<(std::ostream&, const ContainerType<ValueType, Args ...>&) [with ContainerType = std::__cxx11::basic_string; ValueType = char; Args = {std::char_traits<char>, std::allocator<char>}; std::ostream = std::basic_ostream<char>]’
16 | std::ostream& operator <<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
| ^~~~~~~~
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/bits/locale_classes.h:40,
from /usr/include/c++/9/bits/ios_base.h:41,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from prove.cpp:1:
/usr/include/c++/9/bits/basic_string.h:6419:5: note: candidate: ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
6419 | operator<<(basic_ostream<_CharT, _Traits>& __os,
| ^~~~~~~~
I think that the problem is due to the fact that this overload is used to print also simple std::string objects, but I didn't find any suitable way to solve this so far. Any help? thanks.
Your operator<< overload may match types for which an operator<< overload is already defined. I suggest that you disable it for such types:
#include <type_traits>
template <template <typename, typename...> class ContainerType,
typename ValueType, typename... Args>
std::enable_if_t<!is_streamable_v<ContainerType<ValueType, Args...>>,
std::ostream&>
operator<<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
for (const auto& v : c) {
os << v << ' ';
}
return os;
}
The enable_if_t line uses SFINAE to disable the function for types that are already streamable.
The type trait is_streamable_v that is used above could look like this:
template<class T>
struct is_streamable {
static std::false_type test(...);
template<class U>
static auto test(const U& u) -> decltype(std::declval<std::ostream&>() << u,
std::true_type{});
static constexpr bool value = decltype(test(std::declval<T>()))::value;
};
// helper variable template:
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
Your original program should now work as expected:
int main() {
std::vector <int> v = { 1,2,3 };
std::cout << v;
std::map <int,int> m = { { 1, 1} , { 2, 2 }, { 3, 3 } };
std::cout << m;
std::string s = "Test";
std::cout << s;
}
Demo
The issue is that your << is too generic. It matches std::cout << some_string; and potentially other types for which there is already a << overload. The solution is to not provide overloads for types you do not own (unless it is explicitly permitted).
With minimal changes you can refactor your << to be a named function, then write a wrapper:
template <typename T>
struct my_print {
T& t;
};
template <typename T>
std::ostream& operator<<(std::ostream& out,const my_print<T>& mp) {
// call your named funtion template
do_the_printing(out,mp);
return out;
}
The caveat is that you need to explicitly create the instance:
std::cout << my_print(some_stl_container);
Of course you can also let your function template print directly and call it like this
do_the_print(std::cout,some_stl_container);
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 have a class as shown below:
template <class T>
class outer {
public:
typedef T TType;
std::vector <TType> v;
int f1 (TType t) {cout << "t= " << t << endl;};
class inner {
public:
inner& operator=(const inner &in) {return *this;}
void f2(const inner &in) {cout << "in f2\n";}
};
inner read() {cout << "in read\n";return inner();}
};
Outer has to have nested inner. I have to create a Base class for outer (We are going backwards here !!). I should be able to derive outer1 from the Base. Existing clients of outer should work without changing anything. outer should just add code to derive from the base class.
My solution to this is:
template <typename T>
class outer_iface {
public:
typedef T TType;
std::vector <TType> v;
virtual int f1(TType t) {cout << "bt= " << t << endl;};
template <typename U>
class inner_iface {
public:
using value_type = U;
inner_iface& operator=(const inner_iface &in)
{
return static_cast <U*> (this)->operator=(in);
}
void f2(const inner_iface &in)
{
return static_cast <U*> (this)->f2(in);
}
}; //inner_biface
/*template <typename U>
typename inner_iface <U>::value_type read()
{
return static_cast <U*> (this)->read();
}*/
};
template <typename T>
class outer : public outer_iface <T> {
public:
typedef T TType;
std::vector <TType> v;
int f1 (TType t) {cout << "t= " << t << endl;};
class inner : public outer_iface <T> :: template inner_iface <inner> {
public:
inner& operator=(const inner &in) {return *this;}
void f2(const inner &in) {cout << "in f2\n";}
};
inner read() {cout << "in read\n";return inner();}
};
This compiles and builds. But, I have 2 questions:
is my declaration/definition of read correct in outer_iface?
how can I instantiate an outer_iface, with say int type, and call read ?
I tried from main():
outer_iface<int> oi;
oi.read();
clang gave errors:
g++ -g --std=c++11 test7.cpp
test7.cpp: In function ‘int main()’:
test7.cpp:62:11: error: no matching function for call to
‘outer_iface<int>::read()’oi.read();
^
test7.cpp:62:11: note: candidate is:
test7.cpp:28:40: note: template<class U> typename
outer_iface<T>::inner_iface<U>::value_type outer_iface<T>::read()
[with U = U; T = int]
typename inner_iface <U>::value_type read()
^
test7.cpp:28:40: note: template argument deduction/substitution failed:
test7.cpp:62:11: note: couldn't deduce template parameter ‘U’
oi.read();
So, obviously I don't have it right. How can I fix inner_face::read ?
Any help/insight is appreciated.
thanks
sdp
It seems that you want something like:
template <typename T>
class outer_iface {
public:
// ...
typename T::inner read()
{
// std::cout << "in read\n"; return typename T::inner();
return static_cast<T*>(this)->read();
}
};
I have a class hierarchy that can be written to an object using operator<<. The example looks as follows:
#include <iostream>
struct Base
{
};
struct Derived : public Base
{
};
struct Shift
{
template< typename U >
Shift& operator<<(const U&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
#if 0
Shift& operator<<(const Derived&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
#endif
};
int main()
{
Shift sh;
Base bas;
Derived der;
int u32 = 0;
sh << bas;
sh << der;
sh << u32;
}
This produces the following output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const U&) [with U = Derived]
Shift& Shift::operator<<(const U&) [with U = int]
If I uncomment the #if 0 section it will change to the desired output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Derived&)
Shift& Shift::operator<<(const U&) [with U = int]
I have a lot of derived classes (actually a whole hierarchy) and until now I have to write a separate definition of operator<< for all those types. What I'd like is to have a solution where the operator for the base type is called for all types that are derived from Base. Is that possible?
P.S.: I tried several solutions, for example writing a helper class:
struct Base
{
};
struct Derived : public Base
{
};
template< typename T >
struct Helper
{
static void shift()
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
};
template< >
struct Helper< Base >
{
static void shift()
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
};
struct Shift
{
template< typename U >
Shift& operator<<(const U& value)
{
Helper< U >::shift();
return *this;
}
};
Output:
static void Helper<Base>::shift()
static void Helper<T>::shift() [with T = Derived]
static void Helper<T>::shift() [with T = int]
But still the base template is called, instead of the Base specialization.
P.P.S.: I'm currently limited to C++03 without Boost, unfortunately.
In your current implementation the templated overload is preferred as per §13.3.3.1.4 [over.ics.ref]/p1:
When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).
Since the derived-to-base Conversion is given a Conversion rank, to achieve the desired output the templated version with an identity conversion rank must be excluded from the set of viable functions during the overload resolution by using enable_if with a proper condition (SFINAE):
#include <type_traits>
//...
template <typename U>
auto operator<<(const U&)
-> typename std::enable_if<!std::is_base_of<Base, U>{}, Shift&>::type
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Base&)
typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type Shift::operator<<(const U&) [with U = int; typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type = Shift&]
In c++03 the implementation is less readable:
template <bool B, typename T = void>
struct enable_if { typedef T type; };
template <typename T>
struct enable_if<false, T> {};
template <typename Base, typename Derived>
struct is_base_of
{
typedef char (&yes)[1];
typedef char (&no)[2];
static yes test(Base*);
static no test(...);
static const bool value = sizeof(test((Derived*)0)) == sizeof(yes);
};
template <typename Base, typename Derived>
const bool is_base_of<Base, Derived>::value;
//...
template <typename U>
typename enable_if<!is_base_of<Base, U>::value, Shift&>::type operator<<(const U&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}