/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
Откройте диалоговое окно Страницы свойств проекта. Подробнее см. в статье Настройка компилятора C++ и свойства сборки в Visual Studio.
Перейдите на страницу свойств Свойства конфигурации>C/C++>Командная строка.
Измените свойство "Дополнительные параметры", чтобы включить
/Zc:twoPhase-
и нажмите кнопку "ОК".