Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Вывод аргумента шаблона для шаблонов классов в С++ 17: я делаю это неправильно?

Автор вопроса: GeorgОтветов: 1

Согласно https://gcc.gnu.org/projects/cxx-status.html, версия 7 g++, используемая с флагом -std=c++1z, поддерживает вывод аргументов шаблона для шаблонов классов.

Я ожидаю компиляции следующего кода, тем более что Base является абстрактным классом, поэтому:
1. компилятор знает, что экземпляр Base не может быть создан;
2. указатель на базу pt_base указывает на четко определенный экземпляр (т.е. Derived<int>{42}), где тип (int) является явным.

template<typename ValueType>
class Base {
public:
    virtual ValueType getValue() = 0;
};

template<typename ValueType>
class Derived : public Base<ValueType>{
public:
    Derived(ValueType argt){ value = argt; }
    virtual ValueType getValue(){ return value; }
    ValueType value;
};

int main(){
    Base *pt_base = new(Derived<int>{42}); // *ERROR*
    delete pt_base;
}

Тем не менее, он не компилируется. G++ жалуется, что "за типом заполнителя шаблона 'Base' должен следовать простой идентификатор-декларатора"; если я правильно понимаю, он не выводит аргумент шаблона.
Жаль, потому что я хотел бы динамически решать, на какой производный класс pt_base указывает (может быть объект из класса Derived<someType> или из класса Derived2<someType2>). Таким образом, массив или vector<Base *> могут хранить указатели на объекты различных производных классов.

GCC имеет только экспериментальную поддержку для C++17, и у меня нет доступа другому компилятору, поэтому, хотя я получаю ошибку компиляции, я не уверен, что мой код неверен. Как вы думаете?
И как мы можем динамически решить, что pt_base указывает на объект либо из Derived<someType>, либо из Derived2<someType2> (чтобы можно было использовать полиморфизм)?

  • В этом сообщении об ошибке утверждается, что * в Base *pt_base не разрешено. *pt_base является декларатором, но не идентификатором-декларатором (например, недекорированным идентификатором). Однако я не могу найти это правило нигде в N4687. aschepler 07.09.2017
  • @aschepler Правила дедукции срабатывают только в определенных местах. А в исходном предложении явно говорилось такие вещи, как указатели, функции и ссылки. Barry 07.09.2017
  • Кроме того, вывод класса шаблона происходит во время компиляции. В ситуациях, когда вам разрешено написать только Base, компилятор решит, действительно ли это означает Base<someType> или что-то еще. Base не является типом. Таким образом, у вас не может быть ни одной переменной, указывающей на Derived<someType> или Derived<someType2> во время выполнения, если только они оба не наследуют какой-то общий тип. Вам может понадобиться std::any или std::variant для возвращаемого типа его getValue(). aschepler 07.09.2017
  • Если бы vector<Base*> был допустимым, стирание типа поддерживалось бы языком напрямую... Walter 07.09.2017
  • @Barry [dcl.type.class.deduct] Если заполнитель для выведенного типа класса появляется как decl-specifier в decl-specifier-seq объявления инициализации переменной заполнитель заменяется возвращаемым типом функции, выбранной разрешением перегрузки для вывода шаблона класса ([over.match.class.deduct]). Согласно письму, это предложение будет применяться. Хотя в этом примере это потерпит неудачу, потому что ни один из придуманных конструкторов для Base не может принимать указатель. Но при строгом чтении я думаю, что std::vector&& v(1,1); может быть разрешено. Мне кажется Дефект. aschepler 07.09.2017
  • @Barry Эта статья, безусловно, самая плохая письменная работа, которую я когда-либо читал. Но тогда я не очень люблю всю эту функцию. T.C. 07.09.2017

c++templatesg++c++17

Ответы

Ответ 1 · рейтинг 10 · Barry

Вывод аргумента шаблона класса работает для объявления экземпляров типов классов:

Derived d(42);

Или новые выражения:

auto p = new Derived(42);

Или приведения в стиле функций:

foo(Derived(42));

Это не работает для объявления указателей.


Вам нужно будет просто указать аргументы шаблона, как всегда. Или, я думаю:

template <class T> Base<T>* downcast(Base<T>* p) { return p; }
auto pt_base = downcast(new Derived(42));

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы