Вычисление шаблона не удалось из кучи, но работает в стеке

У меня проблема с шаблоном. Когда я создаю экземпляр класса в стеке, он работает. Когда я создаю экземпляр одного класса в куче, он терпит неудачу. (аргумент дедукции) Я не понимаю, почему...

Информация: Я использую gcc 7.2.0 с С++ 17.

вот пример:

#include <iostream>
#include <cstdlib>
#include <memory>

template <class ReturnType, class ClassName, class... Args>
class MethodPtr
{
public:
    typedef ReturnType (ClassName::*Method)(Args...);

    MethodPtr(ClassName* ptr, Method m) : _p(ptr), _m(m)
    {
        (ptr->*m)(4);
    }

    ClassName* _p;
    Method _m;
};

class Example
{
public:
    Example()
    {
        dotest(this, &Example::func);
    }

    template <class Ptr, class Func>
    void dotest(Ptr ptr, Func func)
    {
        // works
        MethodPtr(ptr, func);

        // don't works
        //std::make_unique<MethodPtr>(ptr, func);
        //new MethodPtr(ptr, func);

        //works
        std::make_unique<decltype(MethodPtr(ptr, func))>(ptr, func);
        new decltype(MethodPtr(ptr, func))(ptr, func);
    }

    void func(int i)
    {
        std::cout << i << std::endl;
    }
};

int main()
{
    Example example;
}

У вас есть решение избежать decltype?

Спасибо,

Ответы

Ответ 1

Тот факт, что new MethodPtr(ptr, func) не позволяет сделать вывод, является ошибкой компилятора. Согласно [dcl.type.class.deduct]/2:

Заполнитель для выведенного типа класса также может использоваться в типе-спецификаторе-seq в идентификаторе нового типа или типе-идентификатора нового выражения или в качестве спецификатора простого типа при явном преобразовании типа (функциональный нотация) ([expr.type.conv]). Заполнитель для выведенного типа класса не должен появляться ни в каком другом контексте.

Как вы можете видеть, явное да для нового выражения и полный запрет на все, что явно не разрешено. Поэтому make_unique не может быть присвоен.

Если вы не можете перейти на версию GCC, где это исправлено (или вам просто нужно использовать make_unique), вы не можете избежать decltype. Попробуйте ввести псевдоним типа, чтобы уменьшить неудобства.

Ответ 2

В последних gcc и clang это работает - это new MethodPtr(ptr, func). Так что для gcc7.2 - это ошибка.

Для unique_ptr(new MethodPtr(ptr, func)) - он не может работать, потому что в C++ - на этом уровне, имея MethodPtr* - мы не можем различать unique_ptr<MethodPtr[]> и unique_ptr<MethodPtr> - поэтому он не может быть выводится.

Ответ 3

Посмотрите здесь. Передача просто MethodPtr (шаблон класса) как параметра типа шаблона шаблона функции (std::make_unique) никогда не разрешалась, а вывод шаблона шаблона класса не менял этого.

new MethodPtr{ptr, func}; работает, глядя на ссылку, но я не могу сказать, почему это должно быть иначе, чем new MethodPtr(ptr, func);