Skip to content

Commit de74c34

Browse files
author
Colin Robertson
committed
Updates to permissive-
1 parent ac823b1 commit de74c34

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

docs/build/reference/permissive-standards-conformance.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ By default, the **/permissive-** option is not set. When the option is set, the
5353

5454
The **/permissive-** option sets the [/Zc:strictStrings](../../build/reference/zc-conformance.md) and [/Zc:rvalueCast](../../build/reference/zc-conformance.md) options to conforming behavior. They default to non-conforming behavior. You can pass specific **/Zc** options after **/permissive-** on the command line to override this behavior.
5555

56+
In versions of the compiler beginning in Visual Studio 2017 Update 3, the **/permissive-** option sets the [/Zc:ternary]() option. The compiler also implements more of the requirements for two-phase name look-up. When the **/permissive-** option is set, the compiler parses function and class template definitions, identifying dependent and non-dependent names used in the templates. In this release, only name dependency analysis is performed.
57+
5658
Environment-specific extensions and language areas that the standard leaves up to the implementation are not affected by **/permissive-**. For example, the Microsoft-specific `__declspec`, calling convention and structured exception handling keywords, and compiler-specific pragma directives or attributes are not flagged by the compiler in **/permissive-** mode.
5759

5860
The **/permissive-** option uses the conformance support in the current compiler version to determine which language constructs are non-conforming. The option does not determine if your code conforms to a specific version of the C++ standard. To enable all implemented compiler support for the latest draft standard, use the [/std:latest](../../build/reference/std-specify-language-standard-version.md) option. To restrict the compiler support to more closely match the C++14 standard, use the [/std:c++14](../../build/reference/std-specify-language-standard-version.md) option, which is the default.
@@ -234,6 +236,106 @@ class ATL_NO_VTABLE CFooImpl : public ICustom,
234236
};
235237
```
236238
239+
#### Ambiguous conditional operator arguments
240+
241+
In versions of the compiler before Visual Studio 2017 Update 3, the compiler accepted arguments to the conditional operator (or ternary operator) `?:` that are considered ambiguous by the C++17 standard. In **/permissive-** mode, the compiler now issues one or more diagnostics in cases that compiled without diagnostics in earlier versions.
242+
243+
Commmon errors that may result from this change include:
244+
245+
- error C2593: 'operator ?' is ambiguous
246+
247+
- error C2679: binary '?': no operator found which takes a right-hand operand of type 'B' (or there is no acceptable conversion)
248+
249+
- error C2678: binary '?': no operator found which takes a left-hand operand of type 'A' (or there is no acceptable conversion)
250+
251+
- error C2446: ':': no conversion from 'B' to 'A'
252+
253+
A typical code pattern that can cause this issue is when some class C provides both a non-explicit constructor from another type T and a non-explicit conversion operator to type T. In this case, both the conversion of the 2nd argument to the type of the 3rd and the conversion of the 3rd argument to the type of the 2nd are valid conversions, which is ambiguous according to the standard.
254+
255+
```cpp
256+
// Example 1: class that provides conversion to and initialization from some type T
257+
struct A
258+
{
259+
A(int);
260+
operator int() const;
261+
};
262+
263+
extern bool cond;
264+
265+
A a(42);
266+
// Accepted when /Zc:ternary or /permissive- is not used:
267+
auto x = cond ? 7 : a; // A: permissive prefers A(7) over (int)a
268+
// Accepted always:
269+
auto y = cond ? 7 : int(a);
270+
auto z = cond ? A(7) : a;
271+
```
272+
273+
There is an important exception to this common pattern when T represents one of the null-terminated string types (for example, `const char *`, `const char16_t *`, and so on) and the actual argument to `?:` is a string literal of corresponding type. C++17 has changed semantics from C++14. As a result, the code in example 2 is accepted under /std:c++14 and rejected under /std:c++17 when **/Zc:ternary** or **/permissive-** is used.
274+
275+
```cpp
276+
// Example 2: exception from the above
277+
struct MyString
278+
{
279+
MyString(const char* s = "") noexcept; // from char*
280+
operator const char* () const noexcept; // to char*
281+
};
282+
283+
MyString s;
284+
// Accepted when /Zc:ternary or /permissive- is not used:
285+
auto x = cond ? "A" : s; // MyString: permissive prefers MyString("A") over (const char*)s
286+
// Accepted always:
287+
auto y = cond ? "A" : static_cast<const char*>(s);
288+
```
289+
290+
Another case where you may see errors is in conditional optators with one argument of type `void`. This case may be common in ASSERT-like macros.
291+
292+
```cpp
293+
// Example 3: void arguments
294+
void myassert(const char* text, const char* file, int line);
295+
// Accepted when /Zc:ternary or /permissive- is not used:
296+
#define ASSERT(ex) (void)((ex) ? 0 : myassert(#ex, __FILE__, __LINE__))
297+
// Accepted always:
298+
#define ASSERT(ex) (void)((ex) ? void() : myassert(#ex, __FILE__, __LINE__))
299+
```
300+
301+
You may also see errors in template metaprogramming, where conditional operator result types may change under **/Zc:ternary** and **/permissive-**. One way to resolve this issue is to use `std::remove_reference` on the resulting type.
302+
303+
```cpp
304+
// Example 4: different result types
305+
char a = 'A';
306+
const char b = 'B';
307+
decltype(auto) x = cond ? a : b; // char without, const char& with /Zc:ternary
308+
const char (&z)[2] = argc > 3 ? "A" : "B"; // const char* without /Zc:ternary
309+
```
310+
311+
#### Name dependency analysis
312+
313+
When the **/permissive-** option is set in Visual Studio 2017 Update 3, the compiler parses function and class template definitions, identifying dependent and non-dependent names used in templates as required for two-phase look-up. In this release, only name dependency analysis is performed. In particular, non-dependent names that are not declared in the context of a template definition cause a diagnostic message as required by the ISO C++ standards. However, binding of non-dependent names that require argument dependent look up in the definition context is not done.
314+
315+
```cpp
316+
// dependency.cpp
317+
// For previous behavior, compile with: cl /W4 dependency.cpp
318+
// For name dependency analysis, compile with: cl /W4 /permissive- dependency.cpp
319+
#include <stdio.h>
320+
321+
void f(long) {
322+
puts("Standard two-phase!");
323+
}
324+
325+
template <typename T> void g(T t) {
326+
f(t);
327+
}
328+
329+
void f(int) {
330+
puts("Microsoft one-phase!");
331+
}
332+
333+
int main() {
334+
g(42);
335+
}
336+
```
337+
338+
237339
### To set this compiler option in the Visual Studio development environment
238340

239341
1. Open your project's **Property Pages** dialog box.

0 commit comments

Comments
 (0)