Код шаблона, который компилируется в gcc 8.2, но не в MSVC 19

#c #templates #language-lawyer #crtp

Вопрос:

Этот пример сценария полностью компилируется в gcc 8.2, но в Visual Studio 2019 он возвращается error C3200: 'bar<int>': invalid template argument for template parameter 'bar', expected a class template в строке, где появляется » новое:

 template<typename T, template<typename> class bar>
class foo;

template<typename T>
class bar
{
    friend class foo<T, bar>;
};

template<typename T, template<typename> class bar>
class foo
{
};

class baz : bar<int>
{
public:
    void mash()
    {
        auto whoosh = new foo<int, bar>();
    }
};

int main(int argc, char **argv)
{
    baz A;
    A.mash();

    return 0;
}
 

Читая, я думаю, что проблема может заключаться в том, что второй аргумент шаблона в этой строке теперь является четко определенным типом, а не шаблоном, но даже если это правда, я не знаю, что с этим делать. Замена « bar на « decltype(bar) ни к чему меня не привела.

Я был бы рад любым предложениям.

https://godbolt.org/z/rrGdvf763

Ответ №1:

Я признаю, что не знаю, кто здесь прав или неправ (см. Ниже). Путаница, по-видимому, связана с тем фактом, что внутри шаблона класса baz , наследуемого от bar<int> идентификатора bar , интерпретируется как bar<int> . Странно bar само по себе использование bar ссылки на шаблон не позволяет bar<T> . И составители с этим не согласны. В любом случае, все компиляторы, которые вы включили в представление соответствия, примут код, если вы измените его на:

 class baz : bar<int>
{
public:
    void mash()
    {
        auto whoosh = new foo<int, ::bar>();
    }
};
 

Живая демонстрация

Как отметил Джарод 42:

Из введенного имени класса#In_class_template: «В следующих случаях введенное имя класса обрабатывается как шаблон-имя самого шаблона класса:-оно используется в качестве аргумента шаблона, соответствующего параметру шаблона шаблона». итак, ошибка msvc.

И, благодаря ашеплеру за ссылку, официальную формулировку стандарта можно найти в [temp.local] в пункте 1.

Так что того, что я назвал «странным» выше, следует ожидать. Например:

  template <typename T>
 struct moo {
      moo some_method();
      //^ refers to moo<T>
      foo<int,moo> some_other_method();
      //      ^ refers to moo
 };
 

И то, с чем вы столкнулись, — это случай, когда msvc неправильно реализует это исключение.

Комментарии:

1. Из введенного имени класса#In_class_template : «В следующих случаях введенное имя класса обрабатывается как шаблон-имя самого шаблона класса:-оно используется в качестве аргумента шаблона, соответствующего параметру шаблона шаблона» . итак, ошибка msvc.

2. Да, спасибо, это ключ к разгадке. В мой фактический код, с ‘foo’, ‘bar’ и ‘база’ в пространстве имен, так что я должен сделать, это поставить явного пространства имен классификатор напротив «панели», я.Е «авто-ш-ш-ш = новый foo в<int, ns::bar=»»>();», но затем он компилирует это работает хорошо.

3. Я подумал, что, возможно, это простой вопрос двусмысленности, с одним и тем же символом, используемым как для параметра шаблона, так и для конкретного класса: но я попытался заменить «панель классов шаблона<имя_типа>» на «шаблон<имя_типа><имя_типа> класса U» в объявлении и определении «foo», и это не имело никакого значения.

4. @EosPengwern не в этом проблема, имя аргумента не имеет значения за пределами шаблона.

5. Также в самом стандарте см. [temp.local] , пункт 1.