Overload Resolution between func(const char*) and func(std::string&) - c++

I have two functions:
int func(const std::string& str) { return func(str.c_str()); }
int func(const char* str) { /* ... */ }
If I call func(std::string("abcdef")); first function (const std::string&) call recursively itself, not a const char* because type conversion.
I can use Forward Declaration:
int func(const char* str);
int func(const std::string& str) { return func(str.c_str()); }
int func(const char* str) { /* ... */ }
or change functions definitions order:
int func(const char* str) { /* ... */ }
int func(const std::string& str) { return func(str.c_str()); }
and it works.
But is there another way for Overload Resolution?

To exclude implicit type conversions from overload resolution you should make template functions that only accept exact type matches. (SFINAE) e.g.
template<typename type_t>
auto func(const type_t& string)
-> std::enable_if_t<std::is_same_v<std::string,type_t>,int>
{
}
This will resolve to a function returning an int only when std::string and type_t exactly match (without conversions)

Related

restrict types of function using C++20 Concepts

I've created the following class:
using namespace std;
typedef std::variant<long, bool> value;
class settings {
public:
template<class T>
void set(const string &name, const T &val) noexcept {
data_[name] = val;
}
template<class T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
if (data_.contains(name)) {
if (auto value = get_if<T>(&data_[name]); value) {
return *value;
}
}
return val;
}
private:
unordered_map<string, value> data_;
};
And this works fine, for example with this usage:
auto config = settings{};
config.set("foo", 100L);
config.set("bar", true);
cout << "foo: " << config.get_or_default("foo", 0L) << endl;
cout << "bar: " << config.get_or_default("bar", false) << endl;
cout << "foobar: " << config.get_or_default("foobar", 500L) << endl;
output
foo: 0
bar: 1
foobar: 500
If I try to use the method set with something that is not valid for the type value (bool or long):
auto config = settings{};
config.set("foo", 100.0);
We get an error like this:
\main.cpp(13): error C2679: binary '=': no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
with
[
T=double
]
variant(1200): note: could be 'std::variant<long,bool> &std::variant<long,bool>::operator =(std::variant<long,bool> &&)'
variant(1200): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(const std::variant<long,bool> &)'
variant(1042): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)'
main.cpp(12): note: 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)': could not deduce template argument for '__formal'
main.cpp(13): note: while trying to match the argument list '(std::variant<long,bool>, const T)'
with
[
T=double
]
main.cpp(32): note: see reference to function template instantiation 'void settings::set<double>(const std::string &,const T &) noexcept' being compiled
with
[
T=double
]
Similar using the method get_or_default:
auto config = settings{};
cout << "foo: " << config.get_or_default("foo", 100) <<endl;
We get this error:
variant(1302): error C2338: static_assert failed: 'get_if<T>(variant<Types...> *) requires T to occur exactly once in Types. (N4835 [variant.get]/9)'
main.cpp(17): note: see reference to function template instantiation 'int *std::get_if<int,long,bool>(std::variant<long,bool> *) noexcept' being compiled
main.cpp(32): note: see reference to function template instantiation 'const T &settings::get_or_default<int>(const std::string &,const T &) noexcept' being compiled
with
[
T=int
]
These errors are fine, however I like to use c++ 20 concepts to restrict the types for the method set and get_or_default to give a more simple error to the user of the class that indicates that the types valid are only bool and long.
Is possible as well to give a specific assertion message for those messages using only concepts? Very specifically, not third parties libraries.
Add a declaration:
#include <type_traits>
template<typename T>
concept is_value=std::is_same_v<T,long> || std::is_same_v<T,bool>;
and then make a tiny change to the template:
template<is_value T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
That's it. Make the same change to the other template function.
Not using concept, but you could write your settings in a way that set/get_or_default are not template:
template <typename... Ts>
struct settings_base
{
std::unordered_map<std::string, std::variant<Ts...>> data_;
};
template <typename Base, typename T>
struct settings_node : virtual Base
{
void set(const std::string& name, const T& value)
{
Base::data_[name] = value;
}
[[nodiscard]] const T &get_or_default(const std::string &name, const T &val) noexcept
{
auto it = Base::data_.find(name);
if (it != Base::data_.end()) {
if (auto value = std::get_if<T>(&it->second); value) {
return *value;
}
}
return val;
}
};
template <typename... Ts>
class settings_impl : settings_node<settings_base<Ts...>, Ts>...
{
public:
using settings_node<settings_base<Ts...>, Ts>::set...;
using settings_node<settings_base<Ts...>, Ts>::get_or_default...;
// So equivalent to:
// void set(const std::string&, const T1& value);
// ..
// void set(const std::string&, const TN& value);
};
And then
using settings = settings_impl<bool, long /*std::string*/>;
That allows conversion contrary to template way.
Note though that "xxxx" (const char [N]) would select bool overload and not std::string one, due to overload rules.
Not a concept, but a different approach:
If you want to limit get and set to exactly long or bool, you can have non-template overloads:
class settings {
public:
void set(const std::string &name, const long &val) noexcept {
set_impl(name, val);
}
[[nodiscard]] const long &get_or_default(const std::string &name, const long &val) noexcept {
return get_impl(name, val);
}
void set(const std::string &name, const bool &val) noexcept {
set_impl(name, val);
}
[[nodiscard]] const bool &get_or_default(const std::string &name, const bool &val) noexcept {
return get_impl(name, val);
}
private:
template<class T>
void set_impl(const std::string &name, const T &val) noexcept {
data_[name] = val;
}
template<class T>
[[nodiscard]] const T &get_impl(const std::string &name, const T &val) noexcept {
if (data_.contains(name)) {
if (auto value = get_if<T>(&data_[name])) {
return *value;
}
}
return val;
}
std::unordered_map<std::string, value> data_;
};

error: no matching function for call to second::second, candidates are: second::second(

I need to return the proper type as per the templatized argument. I am getting error as below:
Can someone please suggest whats the solution for this? Thanks in advance.
error: no matching function for call to âsecond::second(const std::basic_string<char,
std::char_traits<char>, std::allocator<char> >&)â
note: candidates are: second::second(const std::string&, const std::string&)
note: second::second(const second&)
Code is as below:
struct first
{
public:
const string &str;
first(const string & str) : str(str) { }
};
struct second : public first
{
public:
const string &str2;
second(const string &str1, const string &str2) : first(str1), str2(str2)
{ }
};
class base
{
public:
template<class T>
inline T fun(const string &s1, const string &s2);// { cout<<" T = "<<a; }
};
template<class T>
inline T base::fun(const string &s1, const string &s2)
{
if(1)
return T(s1);
else
return T(s1, s2);
}
int main()
{
string a = "a";
string bb = "b";
base b;
b.fun<first>(a, bb);
b.fun<second>(a, bb);
return 0;
}
The problem is that you can't create a function template which always accepts two arguments of fixed types, and returns objects of different types depending on template parameter. The reason is that you can't specialize template functions, you can only overload them, and you can't make overloaded functions differ only by return type.
What you can do is use SFINAE. This way at most one function will be present for given template parameter:
class base {
public:
template<typename T, typename = typename std::enable_if<std::is_same<T, first>::value>::type>
first fun(const string &s1, const string &s2) {
return first(s1);
}
template<typename T, typename = typename std::enable_if<std::is_same<T, second>::value>::type>
second fun(const string &s1, const string &s2) {
return second(s1, s2);
}
};
Alternatively you can make base templated and specialize it:
template<typename T> class base;
template<> class base<first> {
public:
static first fun(const string &s1, const string &s2) {
return first(s1);
}
};
template<> class base<second> {
public:
static second fun(const string &s1, const string &s2) {
return second(s1, s2);
}
};
base<first>::fun(a, bb);
base<second>::fun(a, bb);
Demo

implementing a generic binary function with a class and functor as template parameters

I am trying to wrap some templated functions into some binary functors like below. When I try to compile the code I have the error
error: no match for call to ‘(QtyAsc) (myobj&, myobj&)
I thought that being operator() in QtyAsc a function in a class, the template deduction mechanism would have worked but it seems that the compiler doesn't accept myobj classes as valid types for it.
Is it maybe because of the call to boost::bind? I was trying to provide a default implementation for the second templated argument (unfortunately I cannot use C++11 with default templated arguments).
class myobj {
public:
myobj(int val) : qty_(val) {}
int qty() { return qty_;}
private:
int qty_;
};
template<class T>
int get_quantity(const T& o) {
throw runtime_error("get_quantity<T> not implemented");
}
template<>
int get_quantity(const myobj& o) {
return o.qty();
}
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr = boost::bind(&get_quantity<T>,_1)) const {
if(extr(o1) < extr(o2))
return true;
return false;
}
};
int main() {
myobj t1(10),t2(20);
QtyAsc asc;
if(asc(t1,t2))
cout << "Yes" << endl;
}
If you can't use C++11, just provide an additional overload:
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr) const {
return extr(o1) < extr(o2);
}
template<class T>
bool operator()(const T& o1, const T& o2) const {
return operator()(o1, o2, &get_quantity<T>);
}
};
(I've omitted the unnecessary boost::bind.) Also, you will need to declare myobj::qty to be const:
int qty() const {
return qty_;
}
since you want to invoke it on const objects. (Live demo)

How is a method taking const char* as argument a near match to a method taking const int&?

The following code throws compiler error when I compile it.
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// maximum of two C-strings (call-by-value)
inline char const* max (char const* a, char const* b)
{
return strcmp(a,b) < 0 ? b : a;
}
// maximum of three values of any type (call-by-reference)
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c);
}
int main ()
{
::max(7, 42, 68);
}
On compilation I get the error :
error: call of overloaded 'max(const int&, const int&)' is ambiguous
note: candidates are:
note: const T& max(const T&, const T&) [with T =int]
note: const char* max(const char*, const char*)
How does max(const char*, const char*) becomes a near match for max(const int&, const int &) when we have the template method that matches the call?
I bet you have using namespace std in your code. Remove it and you'll be fine.
Compare: http://ideone.com/Csq8SV and http://ideone.com/IQAoI6
If you strictly need namespace std to be used, you can force root namespace call (the way do it in main()):
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max(::max(a,b), c);
}

no matching function for call to

I've this code:
class XMLNode
{
//...
template <typename T>
bool getValue(T& t, const std::string& path) const
{
if (empty())
{
throw std::runtime_error("Empty node");
}
return nsXML::getValue(t, path, *node);
}
template <typename T>
T getValue(const std::string& path) const
{
if (empty())
{
throw std::runtime_error("Empty node");
}
return nsXML::getValue<T>(path, *node);
}
//...
};
class XMLData
{
//...
template <typename T>
T getValue(const std::string& path)
{
return XMLNode(&mDocNode, 0).getValue(path); // ERROR LINE
}
//...
};
And gives me error
no matching function for call to ‘nsXML::XMLNode::getValue(const string&)’
note: candidates are:
note: template<class T> bool nsXML::XMLNode::getValue(T&, const string&) const
note: template<class T> T nsXML::XMLNode::getValue(const string&) const
Why does g++ give me this error?
The compiler has no way to assess with which type you want to instantiate the function template. In this case you have to specify it explicitly:
return XMLNode(&mDocNode, 0).getValue<T>(path);
// ^-- explicit instantiation
Only in some cases the template argument can be deduced by the compiler automatically from the function arguments:
int i;
bool b = XMLNode(&mDocNode, 0).getValue(i, path);
Here, the compiler sees an int as the first function argument and can deduce T for this function call to be int, so its the same as
bool b = XMLNode(&mDocNode, 0).getValue<int>(i, path);
because your XMLNode::getValue(const std::string& path) function is a const, so when it's calling nsXML::getValue, it's looking for the const version, I guess there isn't a const one defined.
Note a const member function and a non const member function are different.