8

Here's my code and the error, and below I'll show some code that works.

#include <iostream>
#include <string>

#include <nlohmann/json.hpp>

using JSON = nlohmann::json;
using std::cout;
using std::endl;
using std::string;

template <class ObjectType>
void dump(const JSON &json) {
    for (auto &[key, value]: json.items()) {
        string foo = value.get<std::string>();
        cout << "Key: " << key;
        cout << " Value: " << foo << endl;
    }
}

int main(int, char **) {
    JSON json;
    json["alpha"] = "beta";
    dump<string>(json);
} 
-$ g++ -std=c++17 Foo.cpp -o Foo && Foo
Foo.cpp: In function ‘void dump(const JSON&)’:
Foo.cpp:14:37: error: expected primary-expression before ‘>’ token
   14 |   string foo = value.get<std::string>();
      |                                     ^
Foo.cpp:14:39: error: expected primary-expression before ‘)’ token
   14 |   string foo = value.get<std::string>();
      |                                       ^

Live demo

If I comment out the template line and change the call in main to dump(json), everything works.

My real problem is actually inside a template class, not a template function, but this is the most simplified version I could create. What I really want is this line:

    ObjectType obj = value.get<ObjectType>();

But that would be the next step.

Does anyone know what I'm doing wrong and how to fix it?

Marek R
  • 32,568
  • 6
  • 55
  • 140
Joseph Larson
  • 8,530
  • 1
  • 19
  • 36
  • 3
    [Can't reproduce](https://gcc.godbolt.org/z/1cjvbxsc1). – HolyBlackCat Jan 24 '22 at 18:06
  • @HolyBlackCat I'm not sure what to tell you. I cut and pasted from a sample. I wonder if it's a version of G++. `g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0` and nlohmann JSON version 3.10.4. OS is Ubuntu 20.04. – Joseph Larson Jan 24 '22 at 18:09
  • 4
    @HolyBlackCat - GCC on godbolt reproduces it (you tried Clang). I think it's a GCC bug. – StoryTeller - Unslander Monica Jan 24 '22 at 18:10
  • 3
    As for how to work around it... `value.template get()` *works*. But by all accounts it shouldn't be needed. `value` is not dependent. – StoryTeller - Unslander Monica Jan 24 '22 at 18:11
  • @StoryTeller-UnslanderMonica You're right. That worked. I don't have a clue what that does, but it worked. If you add an answer and maybe give me a clue what it's doing, I can mark it as answered. – Joseph Larson Jan 24 '22 at 18:18
  • I originally marked it as a duplicate of [this](https://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) since the `template` keyword works. It's only after a closer look that I noticed it shouldn't be needed at all, so that explanation is kinda irrelevant to an answer, @Joseph. I have no idea what's the problem with GCC. – StoryTeller - Unslander Monica Jan 24 '22 at 18:21
  • @StoryTeller-UnslanderMonica Ah. I started looking at the linked answer and didn't understand why it was a duplicate without your info here. Now I see it with the additional context. Thank you so much for the help. – Joseph Larson Jan 24 '22 at 18:25
  • 1
    It compiles if I replace `for (auto &[key, value] : json.items()) { ... }` with `for (auto &pair : json.items()) { auto &[key, value] = pair; ... }`. I don't know why. – Kevin Jan 24 '22 at 18:49
  • @Kevin I should have tried that. The `template` thing is working. Ultimately, I think it's just complicated enough that it's confusing the compiler, and it needed a little hint. – Joseph Larson Jan 24 '22 at 19:06

1 Answers1

2

While it works as-is with MSVC and Clang, in order to get it to compile on GCC without changing the structure or assignment of variables, GCC requires the template disambiguator for dependent types specified in order for it to be correctly parsed.

Snippet:

using JSON = nlohmann::json;

template <class ObjectType>
void dump(const JSON &json) {
    for (auto &[key, value]: json.items()) {
        std::string foo = value.template get<std::string>(); // Here
        std::cout << "Key: " << key;
        std::cout << " Value: " << foo << endl;
    }
}

This is due to value.get<std::string>() being parsed as value.get < ... i.e. value.get followed by the less than operator.

I am wondering whether the reason that this occurs when the function is templated is due to template deduction rules and the existing templating provided for the nlohmann::JSON type which ultimately means the compiler already believes that it has deduced the template type for value.get before parsing the subsequent template specialisation value.get<std::string>.

C2P1
  • 397
  • 2
  • 4
  • 13