Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Surprising behaviour with overloaded operators #2256

Closed
maharmstone opened this issue Jul 9, 2020 · 5 comments
Closed

Surprising behaviour with overloaded operators #2256

maharmstone opened this issue Jul 9, 2020 · 5 comments
Labels
kind: bug state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated

Comments

@maharmstone
Copy link

First of all, thanks for this project - it's amazingly useful.

It looks like you can work around it by using to_json, but I ran into this surprising behaviour with the below code on GCC 10.1 - I'd have expected either both values to be the same, or for the compiler to complain that it's ambiguous. Is what's happening intentional?

#include <string>
#include <iostream>

using namespace std;
using json = nlohmann::json;

class test {
public:
    operator json() const {
        return 42;
    }

    operator string() const {
        return "string";
    }
};

class arr {
public:
    const test& operator[](unsigned int) const {
        return t;
    }

    test t;
};

int main() {
    arr a;

    json j = {
        { "a", a[0] }
    };

    j["b"] = a[1];

    cout << j.dump() << endl;
}

Output: {"a":"string","b":42}

@nlohmann
Copy link
Owner

nlohmann commented Jul 9, 2020

I'm afraid this is another instance of #958...

@gregmarr
Copy link
Contributor

gregmarr commented Jul 9, 2020

I'm not sure this is related to #958, but rather just the way that template argument deduction works.

The arr class isn't useful here, and just causes confusion. This should produce identical results:

#include <string>
#include <iostream>

using namespace std;
using json = nlohmann::json;

class test {
public:
    operator json() const {
        return 42;
    }

    operator string() const {
        return "string";
    }
};

int main() {
    test t;

    json j = {
        { "a", t }
    };

    j["b"] = t;

    cout << j.dump() << endl;
}

In the j["b"] = t; line, j["b"] is a json&. Since t has an operator json, that assignment works and j["b"] is copy-assigned a json object of type integer with the value 42.

In the json j = { { "a", t } }; line, the compiler seems to be creating an initializer_list<std::string> out of the internal {} as that is the only type that can be constructed from both "a" and t.

You should get the same behavior with this:

#include <string>
#include <iostream>

using namespace std;
using json = nlohmann::json;

class test {
public:
    operator string() const {
        return "string";
    }
};

int main() {
    test t;

    json j = {
        { "a", t }
    };

    j["b"] = 42;

    cout << j.dump() << endl;
}

and then this:

#include <string>
#include <iostream>

using namespace std;
using json = nlohmann::json;

int main() {
    json j = {
        { "a", "string" }
    };

    j["b"] = 42;

    cout << j.dump() << endl;
}

If you were expecting j["a"] and j["b"] to be the same, you would probably need to force the compiler to pick the operator from test that you are expecting:

#include <string>
#include <iostream>

using namespace std;
using json = nlohmann::json;

class test {
public:
    operator json() const {
        return 42;
    }

    operator string() const {
        return "string";
    }
};

int main() {
    test t;

    json j = {
        { "a", static_cast<json>(t) }
    };

    j["b"] = t;

    cout << j.dump() << endl;
}

@maharmstone
Copy link
Author

The arr class isn't useful here, and just causes confusion. This should produce identical results:

Nope. Your first snippet was my first attempt at a minimal example, and produces different results: {"a":"string","b":"string"}

@gregmarr
Copy link
Contributor

gregmarr commented Jul 10, 2020

Okay, so it has to do with multiple user defined conversions then. Either way, I would say it's still about template argument deduction.

@stale
Copy link

stale bot commented Aug 9, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated label Aug 9, 2020
@stale stale bot closed this as completed Aug 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: bug state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated
Projects
None yet
Development

No branches or pull requests

3 participants