3

Suppose I have this code:

#include <string>
#include <unordered_set>

struct command_node {
    std::string key, value;
};

bool operator==(const command_node &x, const command_node &y) {
    return x.key == y.key;
}

namespace std {
    template<>
    struct hash<command_node> {
        typedef command_node argument_type;
        typedef size_t result_type;
        size_t operator()(const command_node &x) const {
            return std::hash<std::string>()(x.key);
        }
    };
}

using command_set = std::unordered_set<command_node>;

int main(int argc, char **argv)
{
    command_set commands;

    commands.emplace(
        command_node{"system", "running"}
    );

    return EXIT_SUCCESS;
}

which just creates an unordered_list of a command_node structs. The code is just for illustration.

Question: In main, this works (as shown above):

    commands.emplace(
        command_node{"system", "running"}
    );

but this does not:

    commands.emplace(
        {"system", "running"}
    );

yet if I replace emplace with insert, it works either way. In other words, this works:

    commands.insert(
        {"system", "running"}
    );

Why does emplace not infer command_node?

Blair Fonville
  • 908
  • 1
  • 11
  • 23
  • 3
    Because the `emplace` functions all take arguments by variadic forwarding references, which won't deduce a braced list as anything. – Passer By Jul 26 '18 at 20:01
  • 8
    You should use `commands.emplace("system", "running")`. – Jaa-c Jul 26 '18 at 20:01
  • 2
    @Jaa-c That only works if the type in the container is not an aggregate (and, of course, has a constructor that takes those argument types) – Praetorian Jul 26 '18 at 20:05
  • @Jaa-c That would work for the code above (which was just for a simple demo), but not for my actual project code, where the second argument ("running") is replaced with a container itself. The second argument is where the initializer_list comes into play. – Blair Fonville Jul 26 '18 at 20:07
  • Ok, but then there's no advantage of using emplace... – Jaa-c Jul 26 '18 at 20:28
  • @Jaa-c Well almost. I can at least save myself from one extra construction of const char * to std::string for the first argument. – Blair Fonville Jul 26 '18 at 20:32
  • related/dupe: https://stackoverflow.com/questions/33207232/emplace-back-not-working-with-stdvectorstdmapint-int – NathanOliver Jul 27 '18 at 00:12
  • @NathanOliver Good find. Thanks. – Blair Fonville Jul 27 '18 at 00:15
  • 1
    @BlairFonville you can use `commands.emplace("system", Bar{x,y,z})` in the "real code". Emplace isn't "recursive". Also , these braced lists aren't `initializer_list` s . – M.M Jul 27 '18 at 02:08
  • @M.M Thanks. Yes, that is what I am doing in the project code. Aren’t they, initializer_list’s though? If Bar is a std::vector, or some other container, I thought that’s the overload that {} called. – Blair Fonville Jul 27 '18 at 02:33

1 Answers1

3

This is due to the fact that perfect forwarding based on forwarding references fails with braced initializer lists: to the compiler, this is a "non-deduced context". Instead, pass the arguments as they are passed to the container's underlying value type constructor.

Insert methods differ: they accept either a const value_type& lvalue reference or an rvalue reference (value_type&&). Hence, passing a braced initializer list that initializes the type works out well.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • That makes sense. Regarding: "Instead, pass the arguments as they are passed to the container's underlying value type constructor" - I can't actually do this in my actual code, since my arguments are themselves constructed with initializer lists. But, I get your point. Thanks. – Blair Fonville Jul 26 '18 at 20:14
  • 1
    @lubgr We all better stick to the agreed upon naming by the committee, and use *Forwarding References* instead of *Universal References*: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf – Geezer Aug 12 '18 at 18:29
  • 1
    @SkepticalEmpiricist That's a good hint. I adjusted the term. – lubgr Aug 13 '18 at 14:43