Причудливые ошибки шаблона при компиляции в Visual C ++ с / permissive- - PullRequest
7 голосов
/ 10 апреля 2019

Я пытаюсь скомпилировать некоторый код в VS2019 с / permissive - который включает в себя как шаблоны, так и перегрузки, и происходят странные вещи.(https://godbolt.org/z/fBbQu6)

Как и в Godbolt, когда мой templateFunc () объявляется между двумя перегрузками, например так:

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

Я получаю error C2664: 'void Foospace::func(Foospace::A *)': cannot convert argument 1 from 'T *' to 'Foospace::A *'

Если япереместите templateFunc () ниже перегрузок, это, очевидно, работает:

namespace Foospace {
    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    void func() { Foospace::templateFunc<B>(); }
}

И если я переместлю templateFunc () до обеих перегрузок, это также работает:

namespace Foospace {
    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

И если я сохраню templateFunc() между двумя перегрузками, но просто удалите спецификатор Foospace namespace из вызова func (), и вдруг это тоже сработает:

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

Что здесь происходит?

Ответы [ 2 ]

4 голосов
/ 10 апреля 2019

Здесь много правил C ++.

Как выразился Херб Саттер , " сложность 9/10 ".

Давайте рассмотрим случаи один за другим.

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

Здесь у нас есть квалифицированный поиск внутри шаблона.Имя Foospace::func ищется и «связывается» в точке определения шаблона .В это время известен только func(A*), и, следовательно, последующий вызов func(B*) завершится неудачно.Компилятор прав, чтобы отклонить код.Обратите внимание, что это не было реализовано в MSVC до недавнего времени.

namespace Foospace {
    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    void func() { Foospace::templateFunc<B>(); }
}

Та же история, только результаты поиска в наборе перегрузки func(A*), func(B*).Таким образом, оба вызова успешны.

namespace Foospace {
    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

Здесь поиск ничего не находит.Код не должен компилироваться.По какой-то причине MSVC компилирует его, что предполагает либо ошибку, либо функцию / расширение.GCC и clang отклоняют его .

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

Неквалифицированный поиск здесь.Применяются правила ADL, поэтому во время определения шаблона невозможно сказать, разрешает func(T*) или нет, поэтому поиск фазы 1 всегда позволяет продолжить.Имя ищется в точке создания шаблона , когда оба объявления известны.Код компилируется нормально.

1 голос
/ 10 апреля 2019

Существуют некоторые проблемы с MSVC и двухфазным поиском.

В Visual Studio 2017 версии 15.3 и более поздних по умолчанию компилятор использует двухфазный поиск имени для разрешения имени шаблона.

Параметр /permissive- неявно задает поведение компилятора двухфазного поиска , соответствующее , но его можно переопределить с помощью /Zc:twoPhase-.

Если указана опция /Zc:twoPhase-, компилятор возвращается к своему предыдущему разрешающему шаблону класса и разрешению имени шаблона функции и поведению подстановки.

С этой опцией код компилируется.См. демонстрация Godbolt.

Связанные отчеты об ошибках:

  1. Реализация правильного двухфазного поиска для шаблонов C ++

  2. Использование / permissive- в проекте C ++ / CLI вызывает двухфазные предупреждения о поиске имен

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...