I tried to understand how ADL works, at least its basics, and created following piece of code:
#include <iostream>
#include <string>
#include <vector>
using std::pair;
using std::string;
using std::vector;
namespace My {
using std::to_string;
/** With these forward declarations it works ...
string to_string(char arg);
const string& to_string(const string& str);
template <typename T>
string to_string(const vector<T>& rhs);
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs);
struct A;
string to_string(const A& rhs);
*/
string to_string(char arg)
{
return string(1, arg);
}
const string& to_string(const string& str)
{
return str;
}
template <typename T>
string to_string(const vector<T>& rhs)
{
string str("");
for (const auto& e : rhs) {
str += to_string(e) + " "; //< this fails with `pair<..>`
/// `(to_string)(e)` would fail even with `A`
}
return str;
}
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs)
{
return to_string(rhs.first) + " " + to_string(rhs.second);
}
struct A {
static int counter;
string to_string() const
{
using My::to_string; //< avoid using `A::to_string`
return to_string("<A>[") + to_string(counter++) + to_string("]");
}
};
int A::counter = 0;
string to_string(const A& rhs)
{
return rhs.to_string();
}
}
int main(int /*argc*/, const char* /*argv*/[])
{
using My::to_string;
using My::A;
using std::cout;
using std::endl;
cout << to_string(3.1415) << endl;
cout << to_string(pair<int, char>{5, 'a'}) << endl;
cout << to_string(pair<double, pair<int, int>>{3.14, {1, 2}}) << endl;
cout << to_string(vector<int>{1, 2, 3}) << endl;
cout << to_string(pair<string, vector<int>>{"key", {1, 2, 3}}) << endl;
cout << to_string(pair<string, A>{"key", {}}) << endl;
cout << to_string(vector<A>{{}, {}, {}}) << endl;
/// this will fail to compile
cout << to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}}) << endl;
return 0;
}
I figured out that, inside of My, using std::to_string will use std:: free function if it exists and will use My:: otherwise. Then, outside of namespace My, it is sufficient to do using My::to_string to cover both cases. Fine so far.
Then I used the same technique inside member function A::to_string to avoid preferring the function itself instead of free functions (the same will apply for any other member function).
Finally, I was a little surprised that to_string(vector<A>) compiles, although A is not forward-declared. That's where ADL kicks in, as I've understood. Disabling it (enclosing to_string into brackets) will cause compilation failure.
Now after the long story, here goes my question: why ADL does not work in this case for templated function, i.e. to_string(pair<T1, T2>)? And more importantly, how to fix it? I would be glad if it was not necessary to perform forward declarations, as in my use case the definition of to_string(vector<T>) is located in some base header file and the latter definitions are in different headers and are not supposed to be known at this time.
EDIT:
I tried to somehow "fake" needed forward declarations by some templates or even with some SFINAE, but it either led to ambiguities or to the same result. Finally, I came up with solution which uses member function to_string (but it could be any other name) of desired class. This requires to always implement this function if compatibility with to_string is desired, which in case of STL containers requires to inherit them and add the member function. But I believe that in this case ADL will never fail.
Here are modified parts of the code:
template <typename T,
typename Fun = decltype(&T::to_string),
typename = std::enable_if_t<
std::is_member_function_pointer<Fun>::value>>
string to_string(const T& rhs)
{
return rhs.to_string();
}
template <typename T>
struct Vector : public vector<T> {
using vector<T>::vector;
string to_string() const
{
using My::to_string; //< avoid using `Vector::to_string`
string str("");
for (const auto& e : *this) {
str += to_string(e) + " ";
}
return str;
}
};
template <typename T1, typename T2>
struct Pair : public pair<T1, T2> {
using pair<T1, T2>::pair;
string to_string() const
{
using My::to_string; //< avoid using `Pair::to_string`
return to_string(this->first) + " " + to_string(this->second);
}
};
However, one has to replace vectors and pairs with Vectors and Pairs. (Free function to_string(A) is not needed any more).
Other solutions, comments?
why ADL does not work in this case for templated function, i.e. to_string(pair<T1, T2>)?
ADL works by inspecting the namespaces associated with the types involved in a given call. It then will consider the appropriate overloads found in those namespaces and select the best one.
to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}})
This call will select the overload My::to_string(const vector<T>&) and this in turns calls to_string(std::pair<std::string, int>).
ADL inspect then the namespaces associated with std::pair, std::string and int to look for the overload to_string(std::pair<...>). Since no such overload is defined in the namespace std it needs to find the definition previous to the call, but the overload My::to_string(const pair<T1, T2>&) is defined after. That's why you need to forward declare it.
Notice that you need to forward declare it because you also have this:
to_string(pair<string, vector<int>>)
If, on the other hand, you had something like:
to_string(vector<pair<string, My::A>>{{"a", {}}, {"b", {}}})
Then, one of the associated namespaces for the call will be My itself and the overload to_string(std::pair<...>) will be found without the need to forward declare it.
Related
I'm trying to store key-value parameters as string in a class named ModelConfig. Then I would like to automatically convert these values into specific types, either with custom conversion function or with standard functions stod, stof, stoi, and the like.
My class successfully parses parameters if I provide a custom conversion function, but I can't figure how to also accept standard functions. This is my approach:
class ModelConfig
{
public:
ModelConfig(void) = default;
void addParam(std::string pname, std::string pvalue) {
m_params[pname] = pvalue;
}
template <class F, typename... Args, class T = typename std::result_of<F&&(const std::string&, Args...)>::type>
T getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname));
}
private:
std::map<std::string, std::string> m_params;
};
The class above, can be tested with:
#include <iostream>
#include <map>
#include <functional>
#include "ModelConfig.hpp"
int main(void)
{
ModelConfig mc;
mc.addParam("p1_float", "123.4");
mc.addParam("p2_double", "56.7");
mc.addParam("p3_bool", "true");
mc.addParam("p4_int", "-321");
auto functord = [](const std::string& s) {
return std::stod(s);
};
std::cout << mc.getParam("p2_double", functord) << "\n"; // OK.
std::cout << mc.getParam("p2_double", std::stod) << "\n"; // Error.
return 0;
}
How can I modify getParam to accept functions where their first argument is a string but which can have others with default values?
std::stod is overloaded, thus the compiler can't deduce which function to use.
You can use macro to write a generic wrapper:
#define wrapper(f) \
( [] (auto&&... args) -> decltype(auto) { \
return f(std::forward<decltype(args)>(args)...); \
} )
Then call it by:
std::cout << mc.getParam("p2_double", wrapper(std::stod)) << "\n";
An alternative and, IMO, better design is to store values as std/boost::variant<bool, long, double, std::string> and convert it to/from string during I/O. This also detects config file errors early on load, rather than on first value access which could happen much later and crash your application in front of the user.
Requiring the user of this API to always pass a conversion function is cumbersome. You can use boost::lexical_cast for converting strings to T:
#include <string>
#include <iostream>
#include <unordered_map>
#include <boost/lexical_cast.hpp>
struct ConvertProxy {
std::string const* value_;
template<class T>
T as() const {
return boost::lexical_cast<T>(*value_);
}
template<class T>
operator T() const {
return this->as<T>();
}
};
class ModelConfig {
std::unordered_map<std::string, std::string> m_params;
public:
void addParam(std::string pname, std::string pvalue) {
m_params[pname] = pvalue;
}
ConvertProxy getParam(std::string pname) const {
return {&m_params.at(pname)};
}
};
int main() {
ModelConfig mc;
mc.addParam("p1_float", "123.4");
mc.addParam("p2_double", "56.7");
mc.addParam("p3_bool", "true");
mc.addParam("p4_int", "-321");
// Example syntax.
double d1 = mc.getParam("p2_double");
auto d2 = mc.getParam("p2_double").as<double>();
auto d3 = static_cast<double>(mc.getParam("p2_double"));
std::cout << mc.getParam("p2_double").as<double>() << "\n";
std::cout << static_cast<double>(mc.getParam("p2_double")) << "\n";
}
The interface of boost::lexical_cast enables an easy solution here. If you cannot use boost::lexical_cast you should probably code up your own with a similar interface.
You can do this with no third party lib and without using preprocessor directives if you need:
by explicitely casting your standard functions pointers. Standard functions are overloaded for string and wstring so the compiler needs our help to determine which one to apply
and by slightly changing your functor's signature to adapt it to the signature of these standard functions as they have a second parameter.
These changes would be slight actually:
In ModelConfig:
class ModelConfig
{
[...]
// Adapted the functor's signature to comply to standard functions' signatures:
template <class F, typename... Args, class T = typename std::result_of<F && (const std::string&, size_t *)>::type>
T getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname), 0);
}
[...]
};
In main():
int main(void)
{
[...]
// Adapted the functor to standard functions' signature
auto functord = [](const std::string& s, size_t * pos) {
return std::stod(s, pos);
};
// Unchanged, no need
std::cout << mc.getParam("p2_double", functord) << "\n"; // Still OK.
// Cast to determine which overload to use. The typedef helps having things readable.
typedef double(*StandardFunctionSignature)(const std::string&, size_t*);
std::cout << mc.getParam("p2_double", static_cast<StandardFunctionSignature>(std::stod)) << "\n"; // NO Error, it works now.
[...]
}
If you know the signature of the passed in overload set, you can make an additional overload that captures a specific function pointer from that set.
template <class F>
auto getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname));
}
template <class F>
auto getParam(std::string pname, F(*pconv_functor)(const std::string&, std::size_t*)) const
{
return pconv_functor(m_params.at(pname), 0);
}
This has some obvious limitations, but can be useful in certain situations.
Given this code:
#include <string>
#include <vector>
#include <iostream>
template <typename T>
std::string stringify(const T&) {
return "{?}";
}
template <typename T>
std::string proxy(const T& in) {
return stringify(in);
}
// trying to specialize "stringify()"
template <typename T>
std::string stringify(const std::vector<T>& in) {
return "vector specialization!";
}
template <>
std::string stringify(const std::vector<int>& in) {
return "INT vector specialization!";
}
int main() {
std::cout << proxy(1); // calls the 1st
std::vector<int> intVec;
std::cout << proxy(intVec); // calls the 1st
std::vector<double> dblVec;
std::cout << proxy(dblVec); // calls the 1st
return 0;
}
How can I specialize stringify() for vector<> after proxy<>?
Currently I get {?}{?}{?}
If I delete this one - stringify(const std::vector<T>& in) then the vector<int> starts getting called because it would be a specialization of the first.
Then I would get {?}INT vector specialization!{?}
Is there any way to call any of the 2 vector specialization stringification functions from proxy() - if they are defined last - after the proxy() function?
Is there a way to partially specialize on vector<> and still get called from proxy<>?
I don't want to specialize for vector<int>, vector<double>, vector<UserType>...
EDIT: forgot to mention I need this for C++98
First of all, avoid specializing function templates, prefer overloading. See Herb Sutter's article on the potential pitfalls.
Secondly, the issue you're running into involves how name lookup works for dependent names in function templates. Inside proxy<T>, stringify is a dependent name - it depends on T. That name will be looked up at the point of definition of the template (which will find stringify<T>(const T&) and not the other overload) and again at the point of instantiation in the associated namespace of the arguments (which would be std). Neither of those lookups find your other functions.
It's that second part of lookup - the argument-dependent lookup - that we can take advantage of. Let's just stick everything in one namespace (which I'm naming N arbitrarily, feel free to rename as appropriate):
namespace N {
struct helper { };
template <typename T>
std::string stringify(helper, const T&) {
return "{?}";
}
}
template <typename T>
std::string proxy(const T& in) {
return stringify(N::helper(), in);
}
Ok, so far we've changed absolutely nothing. We still get {?} in all cases. But the now we can stick further overloads (not specializations) of stringify still in that namespace but after the definition of proxy:
namespace N {
template <typename T>
std::string stringify(helper, const std::vector<T>& ) {
return "vector overload!";
}
std::string stringify(helper, const std::vector<int>& ) {
return "INT vector overload!";
}
}
Those two overloads will be found by the second phase of the name lookup because N is an associated namespace of helper. Now proxy(intVFec) will find all three overloads of stringify instead of just the one. And now your code prints:
{?}INT vector overload!vector overload!
as desired. None of the above requires C++11.
Right now I use the following piece of code to dummily convert basic types (int, long, char[], this kind of stuff) to std::string for further processing:
template<class T>
constexpr std::string stringify(const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
however I don't like the fact that it depends on std::stringstream. I tried using std::to_string (from C++11's repertoire) however it chokes on char[] variables.
Is there a simple way offering an elegant solution for this problem?
As far as I know the only way of doing this is by specialising the template by the parameter type with SFINAE.
You need to include the type_traits.
So instead of your code use something like this:
template<class T>
typename std::enable_if<std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::to_string(t);
}
template<class T>
typename std::enable_if<!std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::string(t);
}
this test works for me:
int main()
{
std::cout << stringify(3.0f);
std::cout << stringify("Asdf");
}
Important note: the char arrays passed to this function need to be null terminated!
As noted in the comments by yakk you can get rid of the null termination with:
template<size_t N> std::string stringify( char(const& s)[N] ) {
if (N && !s[N-1]) return {s, s+N-1};
else return {s, s+N};
}
Is there a simple way offering an elegant solution for this problem?
Since nobody proposed it, consider using boost::lexical_cast.
This integrates seamlessly with anything that implements std::ostream<< operator and can be extended for custom types.
I'd recommend using enable_if_t and if you're going to take in any single character variables you specialize those:
template<typename T>
enable_if_t<is_arithmetic<T>::value, string> stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!is_arithmetic<T>::value, string> stringify(T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
Here I'm just specializing char. If you need to specialize wchar, char16, or char32 you'll need to do that as well.
Anyway for non-arithmetic types these overloads will default to using ostringstream which is good cause if you've overloaded the extraction operator for one of your classes this will handle it.
For arithmetic types this will use to_string, with the exception of char and anything else you overload, and those can directly create a string.
Edit:
Dyp suggested using whether to_string accepts an argument of T::type as my enable_if_t condition.
The simplest solution is only available to you if you have access to is_detected in #include <experimental/type_traits>. If you do just define:
template<typename T>
using to_string_t = decltype(to_string(declval<T>()));
Then you can set your code up as:
template<typename T>
decltype(to_string(T{})) stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!experimental::is_detected<to_string_t, T>::value, string> (T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
I asked this question to figure out how to use to_string as my condition. If you don't have access to is_detected I'd highly recommend reading through some of the answers cause they are phenomenal: Metaprograming: Failure of Function Definition Defines a Separate Function
I believe, the most elegant solution is:
#include <string>
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
template <typename T>
typename std::enable_if<!std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
Here, if we can construct std::string using T (we check it with help of std::is_constructible<std::string, T>), then we do it, otherwise we use to_string.
Of course, in C++14 you can replace typename std::enable_if<...>::type with much shorter std::enable_if_t<...>. An example is in the shorter version of the code, right below.
The following is a shorter version, but it's a bit less efficient, because it needs one extra move of std::string (but if we do just a copy instead, it's even less efficient):
#include <string>
std::string stringify(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
template <typename T>
std::enable_if_t<!std::is_convertible<T, std::string>::value, std::string>
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
This version uses implicit conversion to std::string then possible, and uses to_string otherwise. Notice the usage of std::move to take advantage of C++11 move semantics.
Here is why my solution is better than the currently most voted solution by #cerkiewny:
It have much wider applicability, because, thanks to ADL, it is also
defined for any type for which conversion using function to_string
is defined (not only std:: version of it), see the example usage below.
Whereas the solution by #cerkiewny only works for the fundamental
types and for the types from which std::string is constructible.
Of course, in his case it is possible to add extra overloads of
stringify for other types, but it is a much less solid solution if
compared to adding new ADL versions of to_string. And chances are
height, that ADL-compatible to_string is already defined in a third party library for a
type we want to use. In this case, with my code you don't need to write any additional code at all to make stringify work.
It is more efficient,
because it takes advantage of C++11 perfect forwarding (by using universal references (T&&) and std::forward).
Example usage:
#include <string>
namespace Geom {
class Point {
public:
Point(int x, int y) : x(x), y(y) {}
// This function is ADL-compatible and not only 'stringify' can benefit from it.
friend std::string to_string(const Point& p) {
return '(' + std::to_string(p.x) + ", " + std::to_string(p.y) + ')';
}
private:
int x;
int y;
};
}
#include <iostream>
#include "stringify.h" // inclusion of the code located at the top of this answer
int main() {
double d = 1.2;
std::cout << stringify(d) << std::endl; // outputs "1.200000"
char s[] = "Hello, World!";
std::cout << stringify(s) << std::endl; // outputs "Hello, World!"
Geom::Point p(1, 2);
std::cout << stringify(p) << std::endl; // outputs "(1, 2)"
}
Alternative, but not recommended approach
I also considered just overloading to_string:
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
to_string(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
And a shorter version using implicit conversion to std::string:
std::string to_string(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
But these have serious limitations: we need to remember to write to_string instead of std::to_string everywhere where we want to use it; also it is incompatible with the most common ADL usage pattern:
int main() {
std::string a = std::to_string("Hello World!"); // error
using std::to_string; // ADL
std::string b = to_string("Hello World!"); // error
}
And it's most probable, there are other problems connected with this approach.
The simplest solution is to overload for the types you want:
using std::to_string;
template<size_t Size>
std::string to_string(const char (&arr)[Size])
{
return std::string(arr, Size - 1);
}
since to_string isn't a template you can't specialize it, but fortunately this is easier.
The code assumes the array is null terminated, but is still safe if it is not.
You may also want to put the using line inside the functions that call to_string if you have strong feelings about where using belongs.
This also has the benefit that if you pass it a non-null-terminated string somehow, it does not have UB as the one argument std::string constructor does.
Although the the question is not of a gimme the code kind, since I already have a solution implemented I thought of sharing it:
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string;
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string;
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string;
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string;
inline auto buildString() -> std::string { return {}; }
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string {
return head + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string {
return std::to_string(head) + buildString(tail...);
}
Usage:
auto gimmeTheString(std::string const &str) -> void {
cout << str << endl;
}
int main() {
std::string cpp_string{"This c++ string"};
char const c_string[] = "this c string";
gimmeTheString(buildString("I have some strings: ", cpp_string, " and ",
c_string, " and some number ", 24));
return 0;
}
I'm trying to put together a very simple logging class which treats certain types, and particularly vectors, specially. I want to have a default behaviour when using the << operator, but modify it in certain cases. The set-up is:
class LoggerStream
{
template <typename ArgType>
LoggerStream & operator<< (const ArgType &arg)
{
// Standard logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const arma::Col<DataType> &arg)
{
// Specialised logging logic
return *this;
}
LoggerStream & operator<< (const double &arg)
{
// Specialised logging logic
return *this;
}
// Other stuff...
};
The double case works fine. The problem is that for subclasses of the vector types, the general-purpose template seems to take priority, and the more specific template gets ignored.
Since all three templated cases have just one generic template parameter I guess the the vector cases aren't considered the most specialised, but if it were considered ambiguous I would expect a compiler error. (It compiles just fine.) So how can I indicate a specialisation but still generalise over the type of the vector elements? Thanks in advance.
I guess this is to do with some detail of how the Col class is implemented. I'm using (and aliasing) arma::Col<T>::fixed<N> as well, but writing a specific overload for that doesn't seem to help. Any ideas welcome.
I cannot reproduce it. This code works as it is supposed to:
#include <iostream>
#include <vector>
using namespace std;
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
// Standard logging logic
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
vector<int> v;
foo << v; // calling the vector specialization
}
I'm answering my own question not because I have a resolution (yet), but to clarify the problem after further investigation. In fact this doesn't seem to be specific to arma::Col; rather, the issue seems to be in the precedence given to the more specific overload when the argument is of a subclass type. A simple example that shows the problem without using Armadillo is
#include <iostream>
#include <vector>
using namespace std;
template <typename DataType>
class MyVector : public std::vector<DataType> {};
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
std::vector<float> v;
foo << v; // Prints "In vector", as expected
MyVector<float> w;
foo << w; // Prints "In general" (but only if the general case exists)
}
So, using the subclass MyVector produces a call to the general function, not the specialised one. But, if the first version is commented out then both calls produce "In vector". So for some reason both are deemed suitable, but the general function is preferred.
What I still don't know is how to stop this from happening without explicitly writing out a load of specific overloads for individual subclass cases...
#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f("bar");
f = "this";
return 0;
}
I have overloaded = operator. I expected f = "this"; statement to call operator=(const string& ss) overload. But it does not. It calls operator=(bool b) overload. Why?
This operator operator=(const string& ss) requires a conversion of a user defined type for the argument (const char* to std::string), whereas the bool version has none and so provides a better match: you get the conversions from built-in types const char[5] to const char* to bool.
As noted by another answer, the pointer-to-char to bool conversion is preferred because it involves no user-defined conversions, while the std::string conversion has a user-defined conversion in it.
C++11 offers the ability to do manual type control.
template<size_t n>
struct enumarated_enum {
private:
enum empty {};
};
template<bool b, size_t n=0>
using EnableIf = typename std::enable_if< b, typename enumerated_enum<n>::empty >::type;
template<typename String, EnableIf< std::is_convertible< String, std::string >::value >... >
Foo& operator=(String&& s) {
cout << "string overload" << "\n";
}
// either this:
template<typename Bool, EnableIf< !std::is_convertible< Bool, std::string >::value && std::is_convertible< Bool, bool >::value, 1 >... >
Foo& operator=(Bool&& b) {
cout << "bool overload" << "\n";
}
// or this:
Foo& operator=(bool b) {
cout << "bool overload" << "\n";
}
where we match a type perfectly if it can be converted to a std::string, and the template doesn't match if it cannot be converted to std::string and other overloads are looked at.
If you have many types you want to be able to support, you have to use long logical forms that describe which of the overloads you want to run, and make sure the others don't accept it (with the not construct above). If you only have two types, the second type can be a traditional overload. The template overload gets first crack at anything that doesn't match the traditional overload exactly...
(The second template argument is a variardic list of numbered enums which cannot exist, whose only purpose is to make sure that the two templates differ in sufficient ways for the compiler not to complain.)
Without delving into C++ standards, on the surface your problem is, you defined your assignment overload with string& and a bool but in your test you're assigning a char* array.
So if you define a string and assign, it will work as expected.
Check it working here : http://codepad.org/owb6noXR
As others have said, an easy fix to this is to simply cast your string to an std::string, when doing the operator, so C++ knows exactly which overload to choose:
#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f((string)"bar");
f = (string)"this";
return 0;
}