Поделиться через


/Zc:twoPhase- (отключить двухфазный поиск имен)

Параметр /Zc:twoPhase- в разделе /permissive-сообщает компилятору использовать исходное, не соответствующее поведению компилятора Microsoft C++ для синтаксического анализа и создания экземпляров шаблонов классов и шаблонов функций.

Синтаксис

/Zc:twoPhase-

Замечания

Visual Studio 2017 версии 15.3 и более поздних версий. В разделе /permissive-компилятор использует поиск двухфазных имен для разрешения имен шаблона. При указании /Zc:twoPhase-компилятор возвращается к предыдущему несоответствующем шаблону класса и разрешению имен шаблона функции и поведению подстановки. Если /permissive- это не указано, поведение, не соответствующее требованиям, по умолчанию.

Файлы заголовков пакета SDK для Windows версии 10.0.15063.0 (Creators Update или RS2) и более ранние версии не работают в режиме соответствия. /Zc:twoPhase- требуется для компиляции кода для этих версий пакета SDK при использовании /permissive-. Версии пакета SDK для Windows, начиная с версии 10.0.15254.0 (Fall Creators Update или RS3), работают правильно в режиме соответствия. Для них не требуется /Zc:twoPhase- вариант.

Используйте /Zc:twoPhase- , если код требует правильной компиляции старого поведения. Настоятельно рекомендуется обновить код, чтобы он соответствовал стандарту.

Поведение компилятора в разделе /Zc:twoPhase-

По умолчанию или в Visual Studio 2017 версии 15.3 и более поздних версий при указании обоих /permissive- элементов /Zc:twoPhase-компилятор использует это поведение:

  • Он анализирует только объявление шаблона, головку класса и список базовых классов. Текст шаблона записывается в виде потока маркеров. Элементы функций, инициализаторы, аргументы по умолчанию или аргументы noexcept не анализируются. Шаблон класса псевдо-экземплярируется в предварительном типе для проверки правильности объявлений в шаблоне класса. Рассмотрим этот шаблон класса:

    template <typename T> class Derived : public Base<T> { ... }
    

    Объявление шаблона, template <typename T>головка class Derivedкласса и список public Base<T> базовых классов анализируются, но текст шаблона фиксируется в виде потока маркеров.

  • При анализе шаблона функции компилятор анализирует только сигнатуру функции. Тело функции никогда не анализируется. Вместо этого он записывается в виде потока маркеров.

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

Другим эффектом этого поведения является разрешение перегрузки. Нестандартное поведение происходит из-за того, как поток токенов расширяется на сайте создания экземпляров. Символы, которые не были видимы в объявлении шаблона, могут отображаться в момент создания экземпляра. Это означает, что они могут участвовать в разрешении перегрузки. Вы можете найти поведение изменений шаблонов на основе кода, который не был виден в определении шаблона, вопреки стандарту.

Рассмотрим для примера такой код:

// zctwophase.cpp
// To test options, compile by using
// cl /EHsc /nologo /W4 zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp

#include <cstdio>

void func(long) { std::puts("Standard two-phase") ;}

template<typename T> void g(T x)
{
    func(0);
}

void func(int) { std::puts("Microsoft one-phase"); }

int main()
{
    g(6174);
}

Ниже приведены выходные данные при использовании режима соответствия по умолчанию, режима соответствия и режима соответствия с /Zc:twoPhase- параметрами компилятора:

C:\Temp>cl /EHsc /nologo /W4 zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- zctwophase.cpp && zctwophase
zctwophase.cpp
Standard two-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

При компиляции в режиме /permissive-соответствия эта программа печатает "Standard two-phase", так как вторая перегрузка func не отображается, когда компилятор достигает шаблона. При добавлении /Zc:twoPhase-программа выводит "Microsoft one-phase". Выходные данные совпадают с тем, что при не указании /permissive-.

Зависимые имена — это имена , зависящие от параметра шаблона. Эти имена имеют поведение подстановки, которое также отличается./Zc:twoPhase- В режиме соответствия зависимые имена не привязаны в точке определения шаблона. Вместо этого компилятор ищет их при создании экземпляра шаблона. Для вызовов функций с зависимым именем функции имя привязывается к функциям, видимым на сайте вызова в определении шаблона. Добавляются другие перегрузки из подстановки, зависящей от аргументов, как в точке определения шаблона, так и в точке создания экземпляра шаблона.

Двухфазный поиск состоит из двух частей: поиск независимых имен во время определения шаблона и поиск зависимых имен во время создания экземпляра шаблона. В разделе /Zc:twoPhase-компилятор не выполняет поиск, зависящий от аргументов, отдельно от неквалифицированного подстановки. То есть это не делает двухфазный поиск, поэтому результаты разрешения перегрузки могут отличаться.

Еще один пример:

// zctwophase1.cpp
// To test options, compile by using
// cl /EHsc /W4 zctwophase1.cpp
// cl /EHsc /W4 /permissive- zctwophase1.cpp
// cl /EHsc /W4 /permissive- /Zc:twoPhase- zctwophase1.cpp

#include <cstdio>

void func(long) { std::puts("func(long)"); }

template <typename T> void tfunc(T t) {
    func(t);
}

void func(int) { std::puts("func(int)"); }

namespace NS {
    struct S {};
    void func(S) { std::puts("NS::func(NS::S)"); }
}

int main() {
    tfunc(1729);
    NS::S s;
    tfunc(s);
}

При компиляции без /permissive-этого кода выводится следующее:

func(int)
NS::func(NS::S)

При компиляции с /permissive-помощью , но без /Zc:twoPhase-этого кода выводится следующее:

func(long)
NS::func(NS::S)

При компиляции с помощью обоих /permissive- и /Zc:twoPhase-этих кодов выводится следующее:

func(int)
NS::func(NS::S)

В режиме /permissive-соответствия вызов разрешает tfunc(1729) перегрузку void func(long) . Он не разрешается для перегрузки void func(int) , как в разделе /Zc:twoPhase-. Причина заключается в том, что неквалифицированный func(int) объявляется после определения шаблона, и он не найден через поиск, зависящий от аргументов. Но void func(S) участвует в поиске, зависящем от аргументов, поэтому он добавляется в набор перегрузки для вызова tfunc(s), даже если он объявлен после шаблона функции.

Обновление кода для двухфазного соответствия

Для более старых версий компилятора не требуются ключевые слова template и typename везде, где требуется стандарт C++ . Эти ключевые слова необходимы в некоторых позициях, чтобы диамбигуировать, как компиляторы должны анализировать зависимое имя во время первого этапа поиска. Например:

T::Foo<a || b>(c);

Соответствующий компилятор анализирует Foo как переменную в области T, то есть этот код является логическим или выражением с T::foo < a левым операндом и b > (c) в качестве правого операнда. Если вы хотите использовать Foo в качестве шаблона функции, необходимо указать, что это шаблон, добавив ключевое template слово:

T::template Foo<a || b>(c);

В версиях Visual Studio 2017 версии 15.3 и более поздних /permissive- /Zc:twoPhase- версий компилятор разрешает этот код без ключевого template слова. Он интерпретирует код как вызов шаблона функции с аргументом a || b, так как он анализирует шаблоны только в ограниченном порядке. Приведенный выше код не анализируется на первом этапе. На втором этапе достаточно контекста, чтобы сказать, что T::Foo это шаблон, а не переменная, поэтому компилятор не применяет использование ключевого слова.

Это поведение также можно увидеть, устраняя ключевое слово typename перед именами в телах шаблона функции, инициализаторами, аргументами по умолчанию и аргументами noexcept. Например:

template<typename T>
typename T::TYPE func(typename T::TYPE*)
{
    /* typename */ T::TYPE i;
}

Если ключевое слово typename не используется в тексте функции, этот код компилируется в разделе /permissive- /Zc:twoPhase-, но не в /permissive- одиночку. Ключевое typename слово необходимо, чтобы указать, что TYPE он зависит. Так как текст не анализируется в разделе /Zc:twoPhase-, компилятор не требует ключевого слова. В /permissive- режиме соответствия код без ключевого typename слова создает ошибки. Чтобы перенести код на соответствие в Visual Studio 2017 версии 15.3 и более поздних версий, вставьте typename ключевое слово, в котором отсутствует.

Аналогичным образом рассмотрим этот пример кода:

template<typename T>
typename T::template X<T>::TYPE func(typename T::TYPE)
{
    typename T::/* template */ X<T>::TYPE i;
}

В /permissive- /Zc:twoPhase- более старых компиляторах компилятору требуется template только ключевое слово в строке 2. В режиме соответствия компилятору теперь также требуется template ключевое слово в строке 4, чтобы указать, что T::X<T> это шаблон. Найдите код, отсутствующий в этом ключевом слове, и предоставьте его, чтобы код соответствовал стандарту.

Дополнительные сведения о проблемах соответствия см. в статье об улучшениях соответствия C++ в Visual Studio и нестандартном поведении.

Установка данного параметра компилятора в среде разработки Visual Studio

  1. Откройте диалоговое окно Страницы свойств проекта. Подробнее см. в статье Настройка компилятора C++ и свойства сборки в Visual Studio.

  2. Перейдите на страницу свойств Свойства конфигурации>C/C++>Командная строка.

  3. Измените свойство "Дополнительные параметры", чтобы включить /Zc:twoPhase- и нажмите кнопку "ОК".

См. также

/Zc (Соответствие)