1

Below is a example (not the entire class, just relevant parts)

class Container {
private:
    size_t m_next_id;
    std::unordered_map m_objects;
public:
    template<typename Type, typename... Args> size_t create(Args... args) { return this->add(new Type (args...)); }
    size_t add(Base * obj) { m_objects.emplace(m_next_id, obj); return m_next_id++; }
}

Say I have the following

class Base;
class A : Base;
class B : Base;
// etc.

Then the following calls work with out error

container.create<B, Vector2f, float>({1, 0}, 0);
container.add(new B({0, 1}, 0));
container.create<B>(Vector2f{0, 1}, 1); // EDIT no initializer list

However the following produce a compile time error

container.create<B>({1, 0}, 1);

Error:

error: no matching function for call to 'Container::create(<brace-enclosed initializer list>, const int&)'
     container.create<B>({1, 0}, 1);
                                  ^
note: candidate: size_t Container::create(Args ...) [with Type = B; Args = {}; size_t = long long unsigned int]
         template<typename Type, typename... Args> size_t create(Args... args) { return this->add(new Type (args...)); }
                                                          ^~~~~~
note:   candidate expects 0 arguments, 2 provided

I'm unsure why the template isn't able to work with this pattern, nor how to fix it so it would work how I want, based on examples I've seen using std emplace functions this should be able to work.

EDIT: Base on the fact that the version with out an initializer list, works fine, I can conclude the problem is because of the initializer list. However I don't see why this would be a problem, shouldn't this be able to construct the appropriate type based on B's constructor where B is invoked?

Glen Fletcher
  • 644
  • 5
  • 21
  • Braced-init list is a non-deduced context. The template parameter cannot be deduced from it. How is the compiler supposed to know that you expect `{1, 0}` to represent a `Vector2f`, and not, say, a `std::pair` or a `std::vector`? – Igor Tandetnik May 08 '18 at 01:04
  • At the point the template functions is invoked it shouldn't need to know that, just that its an init list containing 2 integers, then the where it invokes the constructor it should be able to resolve that the best match for an init list containing two integers is in this case Vector2f, as it already dose in my second working example. – Glen Fletcher May 08 '18 at 09:13
  • Clearly it would appear that C++ doesn't currently support this capability in init lists. However I remember seeing something like this used just not where it was, i.e. a template function that seamed to accept a variable number of init lists and arguments. – Glen Fletcher May 08 '18 at 09:20
  • But why do you expect it to resolve to `Vector2f`, specifically? There are plenty of other classes that can be constructed from a brace-init list of two integers. The two working examples actually mention `Vector2f` at the call site, but the non-working example does not. Compilers have yet to acquire mind-reading abilities. – Igor Tandetnik May 08 '18 at 12:14
  • It dose have to emplate size_t create -> Type=B, Args=(initlist, int), then the return this->add(new Type (args...)); invoked by create would look identical to the usage container.add(new B({0, 1}, 0)); which the compiler dose resolve to new B(Vector2f(0, 1), 0) base on the overloads for the constructor of B. The problem is passing that information through the point of the constructors invocation, which is in the C++ standard. Any way my question was about how I can get around the flaw in the standard. – Glen Fletcher May 09 '18 at 04:25

0 Answers0