Compartir a través de


Mejoras en la conformidad de C++, cambios de comportamiento y correcciones de errores en Visual Studio 2017

Microsoft C/C++ en Visual Studio (MSVC) hace mejoras de conformidad y corrige errores en cada versión. En este artículo se enumeran las mejoras por versión principal y luego por versión. Para saltar directamente a los cambios relativos a una versión específica, use la lista que encontrará a continuación, en este artículo.

En este documento se enumeran los cambios en Visual Studio 2017. Para acceder a una guía de los cambios en Visual Studio 2022, consulte Mejoras de conformidad de C++ en Visual Studio 2022. Para acceder a una guía de los cambios en Visual Studio 2019, consulte Mejoras de conformidad de C++ en Visual Studio 2019. Para obtener una lista completa de las mejoras de conformidad anteriores, consulte Novedades de Visual C++ de 2003 a 2015.

Mejoras de conformidad en Visual Studio 2017 RTW (versión 15.0)

Gracias a la compatibilidad con los elementos constexpr generalizados y la inicialización de miembros de datos no estáticos (NSDMI) para agregados, el compilador de MSVC en Visual Studio 2017 ya tiene la totalidad de las características que se agregaron en el estándar C++14. Sin embargo, al compilador todavía le faltan algunas características de los estándares C++11 y C++98. Vea Conformidad del lenguaje Microsoft C/C++ para consultar el estado actual del compilador.

C++11: Compatibilidad con expresiones SFINAE en más bibliotecas

El compilador continúa mejorando su compatibilidad con la expresión SFINAE. Se requiere para la deducción y sustitución de argumentos de plantilla donde las expresiones decltype y constexpr puedan aparecer como parámetros de plantilla. Para más información, vea Expression SFINAE improvements in Visual Studio 2017 RC (Mejoras de la expresión SFINAE en Visual Studio 2017 RC).

C++14: NSDMI para agregados

Un agregado es una matriz o una clase que no tiene ningún constructor proporcionado por el usuario, ningún miembro de datos no estático privado ni protegido, ninguna clase base ni ninguna función virtual. A partir de C++14, los agregados pueden contener inicializadores de miembro. Para más información, vea Member initializers and aggregates (Inicializadores de miembro y agregados).

C++14: constexpr extendido

Las expresiones declaradas como constexpr ya pueden contener determinados tipos de declaraciones, instrucciones if y switch, instrucciones loop y mutación de objetos cuya duración se haya iniciado durante la evaluación de la expresión constexpr . Ya no es necesario que una función miembro no estática constexpr sea de forma implícita const . Para más información, consulte Relajación de restricciones en funciones constexpr.

C++17: static_assert simplificado

El parámetro de mensaje para static_assert es opcional. Para obtener más información, consulte N3928: ampliación de static_assert, v2.

C++17: atributo [[fallthrough]]

En el modo /std:c++17 y posterior, el atributo [[fallthrough]] se puede usar en el contexto de las instrucciones switch como una sugerencia al compilador de que se prevé el comportamiento fallthrough. Este atributo evita que el compilador emita advertencias en estos casos. Para más información, consulte P0188R0 - Wording for [[fallthrough]] attribute.

Bucles for generalizados basados en intervalos

Los bucles for basados en intervalos ya no necesitan que begin() ni end() devuelvan objetos del mismo tipo. Este cambio permite que end() devuelva un valor de centinela tal como usan los rangos de range-v3 y la especificación técnica de rangos completada pero no publicada. Para obtener más información, vea P0184R0 - Generalizing the Range-Based for Loop.

Inicialización de lista de copia

Visual Studio 2017 genera correctamente errores del compilador relacionados con la creación de objetos mediante listas de inicializadores. Estos errores no se detectaron en Visual Studio 2015 y podrían provocar bloqueos o un comportamiento indefinido en tiempo de ejecución. De acuerdo con N4594 13.3.1.7p1, en copy-list-initialization, el compilador tiene que considerar un constructor explícito para la resolución de sobrecargas. Sin embargo, debe generar un error si se elige esa sobrecarga concreta.

Los dos ejemplos siguientes se compilan en Visual Studio 2015, pero no en Visual Studio 2017.

struct A
{
    explicit A(int) {}
    A(double) {}
};

int main()
{
    A a1 = { 1 }; // error C3445: copy-list-initialization of 'A' cannot use an explicit constructor
    const A& a2 = { 1 }; // error C2440: 'initializing': cannot convert from 'int' to 'const A &'

}

Para corregir el error, use la inicialización directa:

A a1{ 1 };
const A& a2{ 1 };

En Visual Studio 2015, el compilador trataba erróneamente la inicialización de lista de copia como si fuera inicialización de copia regular; solo consideraba la conversión de constructores para la resolución de sobrecarga. En el ejemplo siguiente, Visual Studio 2015 elige MyInt(23). Visual Studio 2017 genera correctamente el error.

// From http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1228
struct MyStore {
    explicit MyStore(int initialCapacity);
};

struct MyInt {
    MyInt(int i);
};

struct Printer {
    void operator()(MyStore const& s);
    void operator()(MyInt const& i);
};

void f() {
    Printer p;
    p({ 23 }); // C3066: there are multiple ways that an object
        // of this type can be called with these arguments
}

Este ejemplo es similar al anterior, pero genera un error diferente. Se realiza correctamente en Visual Studio 2015 y genera un error en Visual Studio 2017 con C2668.

struct A {
    explicit A(int) {}
};

struct B {
    B(int) {}
};

void f(const A&) {}
void f(const B&) {}

int main()
{
    f({ 1 }); // error C2668: 'f': ambiguous call to overloaded function
}

Definiciones de tipo en desuso

Visual Studio 2017 ahora emite la advertencia correcta para las definiciones de tipo en desuso declaradas en una clase o estructura. El ejemplo siguiente se compila sin advertencias en Visual Studio 2015. Genera el error C4996 en Visual Studio 2017.

struct A
{
    // also for __declspec(deprecated)
    [[deprecated]] typedef int inttype;
};

int main()
{
    A::inttype a = 0; // C4996 'A::inttype': was declared deprecated
}

constexpr

Visual Studio 2017 genera correctamente un error si el operando izquierdo de una operación de evaluación condicional no es válido en un contexto constexpr. El siguiente código se compila en Visual Studio 2015, pero no en Visual Studio 2017, donde genera el error C3615:

template<int N>
struct array
{
    int size() const { return N; }
};

constexpr bool f(const array<1> &arr)
{
    return arr.size() == 10 || arr.size() == 11; // C3615 constexpr function 'f' cannot result in a constant expression
}

Para corregir el error, declare la función array::size() como constexpr o quite el calificador constexpr de f.

Tipos de clase pasados a funciones variádicas

En Visual Studio 2017, las clases o structs pasados a una función variádica como printf se deben poder copiar trivialmente. Al pasar objetos de este tipo, el compilador simplemente realiza una copia bit a bit y no llama al constructor ni al destructor.

#include <atomic>
#include <memory>
#include <stdio.h>

int main()
{
    std::atomic<int> i(0);
    printf("%i\n", i); // error C4839: non-standard use of class 'std::atomic<int>'
                        // as an argument to a variadic function.
                        // note: the constructor and destructor will not be called;
                        // a bitwise copy of the class will be passed as the argument
                        // error C2280: 'std::atomic<int>::atomic(const std::atomic<int> &)':
                        // attempting to reference a deleted function

    struct S {
        S(int i) : i(i) {}
        S(const S& other) : i(other.i) {}
        operator int() { return i; }
    private:
        int i;
    } s(0);
    printf("%i\n", s); // warning C4840 : non-portable use of class 'main::S'
                      // as an argument to a variadic function
}

Para corregir el error, puede llamar a una función miembro que devuelva un tipo que se pueda copiar trivialmente

    std::atomic<int> i(0);
    printf("%i\n", i.load());

o bien usar una conversión estática para convertir el objeto antes de pasarlo:

    struct S {/* as before */} s(0);
    printf("%i\n", static_cast<int>(s))

En el caso de las cadenas compiladas y administradas por medio de CString, se debe usar el operator LPCTSTR() proporcionado para convertir un objeto CString en el puntero C esperado por la cadena de formato.

CString str1;
CString str2 = _T("hello!");
str1.Format(_T("%s"), static_cast<LPCTSTR>(str2));

Calificadores cv en la construcción de clases

En Visual Studio 2015, el compilador a veces omite incorrectamente al calificador cv al generar un objeto de clase mediante una llamada al constructor. Este problema puede causar un bloqueo o un comportamiento inesperado en tiempo de ejecución. En el ejemplo siguiente se compila en Visual Studio 2015, pero se genera un error del compilador en Visual Studio 2017:

struct S
{
    S(int);
    operator int();
};

int i = (const S)0; // error C2440

Para corregir el error, declare operator int() como const.

Comprobación de acceso en nombres completos en plantillas

Las versiones anteriores del compilador no comprueban el acceso a nombres completos en algunos contextos de plantilla. Este problema puede interferir con el comportamiento esperado de SFINAE, ya que se espera un error en la sustitución debido a la inaccesibilidad de un nombre. Podría haber provocado un bloqueo o un comportamiento inesperado en tiempo de ejecución, ya que el compilador habría llamado incorrectamente a la sobrecarga incorrecta del operador. En Visual Studio 2017, se genera un error del compilador. El error específico puede variar, pero un error típico es C2672 "no se ha encontrado una función sobrecargada que coincida". El código siguiente se compila en Visual Studio 2015, pero genera un error en Visual Studio 2017:

#include <type_traits>

template <class T> class S {
    typedef typename T type;
};

template <class T, std::enable_if<std::is_integral<typename S<T>::type>::value, T> * = 0>
bool f(T x);

int main()
{
    f(10); // C2672: No matching overloaded function found.
}

Listas de argumentos de plantilla que faltan

En Visual Studio 2015 y versiones anteriores, el compilador no diagnosticaba todas las listas de argumentos de plantilla que faltaban. No notaba cuando la plantilla faltante aparecía en una lista de parámetros de plantilla: por ejemplo, cuando faltaba parte de un argumento de plantilla predeterminado o un parámetro de plantilla sin tipo. Este problema puede provocar un comportamiento impredecible, como el bloqueo del compilador o un comportamiento inesperado en tiempo de ejecución. El siguiente código se compila en Visual Studio 2015, pero genera un error en Visual Studio 2017.

template <class T> class ListNode;
template <class T> using ListNodeMember = ListNode<T> T::*;
template <class T, ListNodeMember M> class ListHead; // C2955: 'ListNodeMember': use of alias
                                                     // template requires template argument list

// correct:  template <class T, ListNodeMember<T> M> class ListHead;

Expresión SFINAE

Para admitir la expresión SFINAE, el compilador ahora analiza argumentos decltype cuando las plantillas se declaran en lugar de crear instancias. Así, si se encuentra una especialización no dependiente en el argumento decltype, no se aplaza hasta el tiempo de creación de instancias. Se procesa inmediatamente y los errores resultantes se diagnostican en ese momento.

En el ejemplo siguiente se muestra este tipo de error del compilador que se genera en el punto de declaración:

#include <utility>
template <class T, class ReturnT, class... ArgsT>
class IsCallable
{
public:
    struct BadType {};

    template <class U>
    static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>

    template <class U>
    static BadType Test(...);

    static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};

constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");

Clases declaradas en espacios de nombres anónimos

Según el estándar de C++, una clase que se declara dentro de un espacio de nombres anónimo tiene vinculación interna y, eso significa que no se puede exportar. En Visual Studio 2015 y versiones anteriores no se aplicó esta regla. En Visual Studio 2017 se aplica la regla de forma parcial. En Visual Studio 2017, el ejemplo siguiente genera el error C2201:

struct __declspec(dllexport) S1 { virtual void f() {} };
  // C2201 const anonymous namespace::S1::vftable: must have external linkage
  // in order to be exported/imported.

Inicializadores predeterminados para miembros de clase de valor (C++/CLI)

En Visual Studio 2015 y versiones anteriores, el compilador permitía (aunque omitía) un inicializador de miembro predeterminado para un miembro de una clase de valor. La inicialización predeterminada de una clase de valor siempre inicializa a cero a los miembros. No se permite un constructor predeterminado. En Visual Studio 2017, los inicializadores de miembro predeterminados generan un error del compilador, tal como se muestra en este ejemplo:

value struct V
{
    int i = 0; // error C3446: 'V::i': a default member initializer
               // isn't allowed for a member of a value class
};

Indizadores predeterminados (C++/CLI)

En Visual Studio 2015 y versiones anteriores, el compilador en algunos casos identificaba incorrectamente una propiedad predeterminada como un indizador predeterminado. Era posible solucionar el problema con el identificador default para acceder a la propiedad. La solución en sí misma se volvió problemática después de que se empezara a usar default como palabra clave en C++11. En Visual Studio 2017, se corrigieron los errores que requerían la solución. El compilador ahora genera un error cuando se utiliza default para acceder a la propiedad predeterminada de una clase.

//class1.cs

using System.Reflection;
using System.Runtime.InteropServices;

namespace ClassLibrary1
{
    [DefaultMember("Value")]
    public class Class1
    {
        public int Value
        {
            // using attribute on the return type triggers the compiler bug
            [return: MarshalAs(UnmanagedType.I4)]
            get;
        }
    }
    [DefaultMember("Value")]
    public class Class2
    {
        public int Value
        {
            get;
        }
    }
}

// code.cpp
#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value; // error
       r1->default;
       r2->Value;
       r2->default; // error
}

En Visual Studio 2017 se puede acceder a ambas propiedades Value por su nombre:

#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value;
       r2->Value;
}

Mejoras de conformidad en la versión 15.3

Expresiones lambda constexpr

Las expresiones lambda ahora se pueden usar en expresiones constantes. Para obtener más información, vea Expresiones lambda constexpr en C++.

if constexpr en plantillas de función

Una plantilla de función puede contener instrucciones if constexpr que habilitan la creación de ramas de tiempo de compilación. Para obtener más información, vea Instrucciones if constexpr.

Instrucciones de selección con inicializadores

Una instrucción if puede incluir un inicializador que introduce una variable en el ámbito de bloque dentro de la instrucción misma. Para más información, vea Instrucciones if con inicializador.

Atributos [[maybe_unused]] y [[nodiscard]]

El nuevo atributo [[maybe_unused]] silencia las advertencias cuando no se usa una entidad. El atributo [[nodiscard]] crea una advertencia si se descarta el valor devuelto de una llamada de función. Para más información, consulte Atributos en C++.

Uso de espacios de nombres de atributo sin repetición

Nueva sintaxis para habilitar solo un identificador de espacio de nombres único en una lista de atributos. Para más información, consulte Atributos en C++.

Enlaces estructurados

En una sola declaración ahora es posible almacenar un valor con nombres individuales para sus componentes, cuando el valor es una matriz, un std::tuple o std::pair o tiene todos los miembros de datos no estáticos públicos. Para más información, vea P0144R0 - Structured Bindings (Enlaces estructurados) y Returning multiple values from a function (Devolver varios valores de una función).

Reglas de construcción para valores enum class

Ahora hay una conversión implícita para las enumeraciones con ámbito que no es de restricción. Convierte un tipo subyacente de la enumeración con ámbito en la enumeración misma. La conversión está disponible cuando su definición no introduce un enumerador y cuando el origen usa una sintaxis de inicialización de lista. Para obtener más información, vea P0138R2 - Construction Rules for enum class Values y Enumeraciones.

Captura de *this por valor

El objeto *this en una expresión lambda ahora se puede capturar por valor. Este cambio permite escenarios en los que se invoca la expresión lambda en operaciones asincrónicas y en paralelo, en particular en las arquitecturas de máquinas más recientes. Para más información, consulte P0018R3 - Lambda Capture of *this by Value as [=,*this].

Quitar operator++ para bool

operator++ ya no se admite en tipos bool. Para obtener más información, vea P0002R1 - Remove Deprecated operator++(bool).

Eliminación de la palabra clave register en desuso

La palabra clave register, anteriormente en desuso (e ignorada por el compilador), ahora se ha eliminado del idioma. Para obtener más información, vea P0001R1 - Remove Deprecated Use of the register Keyword.

Llamadas a plantillas de miembro eliminado

En versiones anteriores de Visual Studio, había ocasiones en que el compilador no podía emitir un error en las llamadas con un formato incorrecto a una plantilla de miembro eliminado. Estas llamadas podrían causar bloqueos en tiempo de ejecución. El código siguiente ahora produce el error C2280:

template<typename T>
struct S {
   template<typename U> static int f() = delete;
};

void g()
{
   decltype(S<int>::f<int>()) i; // this should fail with
// C2280: 'int S<int>::f<int>(void)': attempting to reference a deleted function
}

Para corregir el error, declare i como int.

Comprobaciones de condición previa para type traits

La versión 15.3 de Visual Studio 2017 mejora las comprobaciones de condición previa para type-traits a fin de seguir el estándar de manera más estricta. Una de estas comprobaciones es para asignable. El código siguiente produce el error C2139 en la versión 15.3 de Visual Studio 2017:

struct S;
enum E;

static_assert(!__is_assignable(S, S), "fail"); // C2139 in 15.3
static_assert(__is_convertible_to(E, E), "fail"); // C2139 in 15.3

Nuevas advertencias de compilador y comprobaciones en tiempo de ejecución en la serialización nativa a administrada

La llamada de funciones administradas a funciones nativas requiere la serialización. El CLR realiza la serialización, pero no comprende la semántica de C++. Si se pasa un objeto nativo por valor, CLR llama al constructor de copia del objeto o usa BitBlt, lo que puede provocar un comportamiento indefinido en tiempo de ejecución.

Ahora, el compilador emite una advertencia si encuentra este error en tiempo de compilación: un objetivo nativo con el constructor de copia eliminada se pasa entre los límites nativos y administrados por valor. En aquellos casos en los que el compilador no lo sepa en tiempo de compilación, se inyecta una comprobación en tiempo de ejecución para que el programa llame a std::terminate inmediatamente cuando se produzca una serialización con un formato incorrecto. En la versión 15.3 de Visual Studio 2017, el código siguiente produce la advertencia C4606:

class A
{
public:
   A() : p_(new int) {}
   ~A() { delete p_; }

   A(A const &) = delete;
   A(A &&rhs) {
   p_ = rhs.p_;
}

private:
   int *p_;
};

#pragma unmanaged

void f(A a)
{
}

#pragma managed

int main()
{
    // This call from managed to native requires marshaling. The CLR doesn't
    // understand C++ and uses BitBlt, which results in a double-free later.
    f(A()); // C4606 'A': passing argument by value across native and managed
    // boundary requires valid copy constructor. Otherwise, the runtime
    // behavior is undefined.`
}

Para corregir el error, quite la directiva #pragma managed para marcar el llamador como nativo y evitar la serialización.

Advertencia de API experimental para WinRT

Las API de WinRT que se publican para experimentación y comentarios se decoran con Windows.Foundation.Metadata.ExperimentalAttribute. En la versión 15.3 de Visual Studio 2017, el compilador produce la advertencia C4698 para este atributo. Algunas API de versiones anteriores del SDK de Windows se han decorado con el atributo, y las llamadas a estas API ahora desencadenan esta advertencia del compilador. Los SDK de Windows más recientes tienen el atributo quitado de todos los tipos enviados. Si usa un SDK antiguo, deberá suprimir estas advertencias en todas las llamadas a tipos enviados.

El código siguiente produce la advertencia C4698:

Windows::Storage::IApplicationDataStatics2::GetForUserAsync(); // C4698
// 'Windows::Storage::IApplicationDataStatics2::GetForUserAsync' is for
// evaluation purposes only and is subject to change or removal in future updates

Para deshabilitar la advertencia, agregue #pragma:

#pragma warning(push)
#pragma warning(disable:4698)

Windows::Storage::IApplicationDataStatics2::GetForUserAsync();

#pragma warning(pop)

Definición fuera de línea de una función de miembro de plantilla

La versión 15.3 de Visual Studio 2017 produce un error para una definición fuera de línea de una función de miembro de plantilla que no se ha declarado en la clase. El código siguiente ahora produce el error C2039:

struct S {};

template <typename T>
void S::f(T t) {} // C2039: 'f': is not a member of 'S'

Para corregir el error, agregue una declaración a la clase:

struct S {
    template <typename T>
    void f(T t);
};
template <typename T>
void S::f(T t) {}

Intento de tomar la dirección del puntero this

En C++, this es un prvalue de puntero de tipo a X. No puede tomar la dirección de this ni enlazarlo a una referencia lvalue. En versiones anteriores de Visual Studio, el compilador le permitiría eludir esta restricción mediante el uso de una conversión. En la versión 15.3 de Visual Studio 2017, el compilador genera el error C2664.

Conversión a una clase base inaccesible

La versión 15.3 de Visual Studio 2017 genera un error cuando se intenta convertir un tipo a una clase base que es inaccesible. El código siguiente es incorrecto y podría provocar un bloqueo en tiempo de ejecución. El compilador produce ahora el error C2243 cuando ve código como este:

#include <memory>

class B { };
class D : B { }; // C2243: 'type cast': conversion from 'D *' to 'B *' exists, but is inaccessible

void f()
{
   std::unique_ptr<B>(new D());
}

No se permiten argumentos predeterminados en definiciones fuera de línea de funciones miembro

No se permiten argumentos predeterminados en definiciones fuera de línea de funciones miembro en clases de plantilla. El compilador emitirá una advertencia en /permissive y un error de hardware en /permissive-.

En versiones anteriores de Visual Studio, el siguiente código con formato incorrecto podría generar un bloqueo en tiempo de ejecución. En la versión 15.3 de Visual Studio 2017 se produce la advertencia C5037:

template <typename T>
struct A {
    T f(T t, bool b = false);
};

template <typename T>
T A<T>::f(T t, bool b = false) // C5037: 'A<T>::f': an out-of-line definition of a member of a class template cannot have default arguments
{
    // ...
}

Para corregir el error, quite el argumento predeterminado = false.

Uso de offsetof con el designador de miembro compuesto

En la versión 15.3 de Visual Studio 2017, el uso de offsetof(T, m), donde m es un "designador de miembro compuesto", da lugar a una advertencia al compilar con la opción /Wall. El código siguiente tiene un formato incorrecto y podría provocar un bloqueo en tiempo de ejecución. En la versión 15.3 de Visual Studio 2017 se produce la advertencia C4841:

struct A {
   int arr[10];
};

// warning C4841: non-standard extension used: compound member designator used in offsetof
constexpr auto off = offsetof(A, arr[2]);

Para corregir el código, deshabilite la advertencia con una pragma o cambie el código para que no se use offsetof:

#pragma warning(push)
#pragma warning(disable: 4841)
constexpr auto off = offsetof(A, arr[2]);
#pragma warning(pop)

Uso de offsetof con un miembro de datos estático o una función miembro

En la versión 15.3 de Visual Studio 2017, el uso de offsetof(T, m), donde m hace referencia a un miembro de datos estático o a una función miembro, da error. El código siguiente produce el error C4597:

#include <cstddef>

struct A {
   int ten() { return 10; }
   static constexpr int two = 2;
};

constexpr auto off = offsetof(A, ten);  // C4597: undefined behavior: offsetof applied to member function 'A::ten'
constexpr auto off2 = offsetof(A, two); // C4597: undefined behavior: offsetof applied to static data member 'A::two'

Este código no tiene un formato correcto y podría ocasionar un bloqueo en tiempo de ejecución. Para corregir el error, cambie el código para que deje de invocar un comportamiento indefinido. Se trata de código no portable que no admite el estándar de C++.

Nueva advertencia en los atributos __declspec

En la versión 15.3 de Visual Studio 2017, el compilador ya no ignora los atributos si __declspec(...) se aplica antes de la especificación de vinculación de extern "C". Anteriormente, el compilador ignoraría el atributo, lo que podría tener implicaciones para el tiempo de ejecución. Si se establecen las opciones /Wall y /WX, el código siguiente genera la advertencia C4768:

__declspec(noinline) extern "C" HRESULT __stdcall // C4768: __declspec attributes before linkage specification are ignored

Para corregir la advertencia, coloque primero extern "C":

extern "C" __declspec(noinline) HRESULT __stdcall

Esta advertencia está desactivada de forma predeterminada en la versión 15.3 de Visual Studio 2017, y solo afecta al código compilado con /Wall /WX. A partir la versión 15.5 de Visual Studio 2017, está habilitada de manera predeterminada como una advertencia de nivel 3.

decltype y llamadas a destructores eliminados

En versiones anteriores de Visual Studio, el compilador no detectaba cuándo se produce una llamada a un destructor eliminado en el contexto de la expresión asociada a decltype. En la versión 15.3 de Visual Studio 2017, el código siguiente produce el error C2280:

template<typename T>
struct A
{
   ~A() = delete;
};

template<typename T>
auto f() -> A<T>;

template<typename T>
auto g(T) -> decltype((f<T>()));

void h()
{
   g(42); // C2280: 'A<T>::~A(void)': attempting to reference a deleted function
}

Variables const no inicializadas

La versión Visual Studio 2017 RTW tenía una regresión: el compilador de C++ no emitía un diagnóstico para una variable const no inicializada. Esta regresión se ha corregido en Visual Studio 2017 versión 15.3. El código siguiente ahora produce la advertencia C4132:

const int Value; // C4132: 'Value': const object should be initialized

Para corregir el error, asigne un valor a Value.

Declaraciones vacías

La versión 15.3 de Visual Studio 2017 ahora advierte sobre declaraciones vacías para todos los tipos, no solo los integrados. El código siguiente produce ahora una advertencia C4091 de nivel 2 para las cuatro declaraciones:

struct A {};
template <typename> struct B {};
enum C { c1, c2, c3 };

int;    // warning C4091 : '' : ignored on left of 'int' when no variable is declared
A;      // warning C4091 : '' : ignored on left of 'main::A' when no variable is declared
B<int>; // warning C4091 : '' : ignored on left of 'B<int>' when no variable is declared
C;      // warning C4091 : '' : ignored on left of 'C' when no variable is declared

Para quitar las advertencias, conviértalas en comentario o quite las declaraciones vacías. En casos donde el objeto sin nombre no tenga que tener un efecto secundario (como RAII), se le debe proporcionar un nombre.

La advertencia se excluye en /Wv:18 y está activada de forma predeterminada en el nivel de advertencia W2.

std::is_convertible para tipos de matriz

Las versiones anteriores del compilador daban resultados incorrectos para std::is_convertible con los tipos de matriz. Esto requería que los autores de bibliotecas distinguieran mayúsculas y minúsculas en el compilador de Microsoft C++ al usar el rasgo de tipo std::is_convertible<...>. En el siguiente ejemplo, las aserciones estáticas se validan en versiones anteriores de Visual Studio, pero no en Visual Studio 2017 versión 15.3:

#include <type_traits>

using Array = char[1];

static_assert(std::is_convertible<Array, Array>::value);
static_assert(std::is_convertible<const Array, const Array>::value, "");
static_assert(std::is_convertible<Array&, Array>::value, "");
static_assert(std::is_convertible<Array, Array&>::value, "");

std::is_convertible<From, To> se calcula comprobando si una definición de función imaginaria está bien formada:

   To test() { return std::declval<From>(); }

Destructores privados y std::is_constructible

Las versiones anteriores del compilador omitían si un destructor era privado al decidir el resultado de std::is_constructible. Ahora sí se tiene en cuenta. En el siguiente ejemplo, las aserciones estáticas se validan en versiones anteriores de Visual Studio, pero no en Visual Studio 2017 versión 15.3:

#include <type_traits>

class PrivateDtor {
   PrivateDtor(int) { }
private:
   ~PrivateDtor() { }
};

// This assertion used to succeed. It now correctly fails.
static_assert(std::is_constructible<PrivateDtor, int>::value);

Los destructores privados hacen que un tipo sea no construible. std::is_constructible<T, Args...> se calcula como si se hubiera escrito la declaración siguiente:

   T obj(std::declval<Args>()...)

Esta llamada implica una llamada al destructor.

C2668: Ambiguous overload resolution (resolución de sobrecarga ambigua)

A veces, las versiones anteriores del compilador no eran capaces de detectar la ambigüedad cuando encontraban varios candidatos por medio tanto de declaraciones como de búsquedas dependientes de argumentos. Este error puede hacer que se elija la sobrecarga incorrecta y que ello derive en un comportamiento inesperado del entorno de ejecución. En el ejemplo siguiente, la versión 15.3 de Visual Studio 2017 genera correctamente la advertencia C2668:

namespace N {
   template<class T>
   void f(T&, T&);

   template<class T>
   void f();
}

template<class T>
void f(T&, T&);

struct S {};
void f()
{
   using N::f;

   S s1, s2;
   f(s1, s2); // C2668: 'f': ambiguous call to overloaded function
}

Para corregir el código, prescinda del uso de la instrucción N::f si lo que quiere es llamar a ::f().

C2660: Declaraciones de función locales y búsquedas dependientes de argumentos

Las declaraciones de función locales ocultan la declaración de función en el ámbito de inclusión y deshabilitan la búsqueda dependiente de argumentos. En este caso, las versiones anteriores del compilador siempre hacían que se realizara una búsqueda dependiente de argumentos. Si el compilador elegía una sobrecarga equivocada, esto podía provocar un comportamiento inesperado del entorno de ejecución. Este error suele deberse a una firma incorrecta de la declaración de función local. En el ejemplo siguiente, la versión 15.3 de Visual Studio 2017 genera correctamente la advertencia C2660:

struct S {};
void f(S, int);

void g()
{
   void f(S); // C2660 'f': function does not take 2 arguments:
   // or void f(S, int);
   S s;
   f(s, 0);
}

Para corregir el problema, cambie la firma f(S) o quítela.

C5038: Orden de inicialización en las listas de inicializadores

Los miembros de clase se inicializan en el orden en que se declaran, no en el orden en que aparecen en las listas de inicializadores. Las versiones anteriores del compilador no avisaban cuando el orden de la lista de inicializadores difería del orden de declaración. Este problema podía provocar un comportamiento indefinido del tiempo de ejecución cuando la inicialización de un miembro dependía de otro miembro de la lista ya en proceso de inicialización. En el ejemplo siguiente, la versión 15.3 de Visual Studio 2017 (con /Wall) genera la advertencia C5038:

struct A
{    // Initialized in reverse, y reused
    A(int a) : y(a), x(y) {} // C5038: data member 'A::y' will be initialized after data member 'A::x'
    int x;
    int y;
};

Para corregir el problema, organice la lista de inicializadores de forma que tenga el mismo orden que las declaraciones. Cuando uno o ambos inicializadores hacen referencia a miembros de clase base, se genera una advertencia similar.

Esta advertencia está desactivada de forma predeterminada y solo afecta al código compilado con /Wall .

Mejoras de conformidad en la versión 15.5

Las características marcadas con [14] están disponibles sin condiciones, incluso en el modo /std:c++14.

Nuevo conmutador de compilador para extern constexpr

En versiones anteriores de Visual Studio, el compilador siempre proporcionaba una vinculación interna de variable de constexpr aunque la variable se marcase como extern . En la versión 15.5 de Visual Studio 2017, el nuevo conmutador de compilador, /Zc:externConstexpr, permite un comportamiento correcto que cumple con los estándares. Para obtener más información, consulte Vinculación de extern constexpr.

Eliminación de las especificaciones de excepciones dinámicas

P0003R5 Las especificaciones de excepción dinámica se dejaron en desuso en C++11. La característica se quitó de C++17, pero la especificación throw(), que (todavía) está en desuso, se mantiene estrictamente como un alias de noexcept(true). Para obtener más información, consulte Eliminación de las especificaciones de excepciones dinámicas y noexcept.

not_fn()

P0005R4 not_fn es un sustituto de not1 y not2.

Nueva redacción de enable_shared_from_this

P0033R1enable_shared_from_this se agregó en C++11. En la versión estándar de C++17 se actualiza la especificación para controlar mejor determinados casos extremos. [14]

Inserción de asignaciones y conjuntos

P0083R3 Esta característica permite la extracción de nodos de los contenedores asociativos (por ejemplo, map, set, unordered_map o unordered_set) que posteriormente se pueden modificar y volver a insertar en el mismo contenedor o en otro que use el mismo tipo de nodo. Un caso de uso habitual es extraer un nodo de un std::map, cambiar la clave y volverlo a insertar.

Desuso de vestigios de elementos de biblioteca

P0174R2 Varias características de la biblioteca estándar de C++ se han reemplazado por características nuevas a lo largo de los años, o bien se ha detectado que eran problemáticas o no eran útiles. Estas características están oficialmente en desuso en C++17.

Eliminación de la compatibilidad con el asignador en std::function

P0302R1 Antes de C++17, la plantilla de clase std::function tenía varios constructores que usaban un argumento asignador. Sin embargo, el uso de argumentos allocator en este contexto era problemático, y la semántica no era clara. Se han quitado los constructores del problema.

Correcciones para not_fn()

P0358R1 La nueva redacción de std::not_fn ofrece más posibilidades para la propagación de categorías de valor cuando se usa en una invocación de contenedores.

shared_ptr<T[]>, shared_ptr<T[N]>

P0414R2 Los cambios de shared_ptr de Elementos fundamentales de biblioteca se combinan en C++17. [14]

Corrección de shared_ptr para las matrices

P0497R0 Correcciones de la compatibilidad de shared_ptr con las matrices. [14]

Aclaración de insert_return_type

P0508R0 Los contenedores asociativos con claves únicas y los contenedores desordenados con claves únicas tienen una función de miembro insert que devuelve el tipo anidado insert_return_type. Ahora, este tipo de valor devuelto se define como la especialización de un tipo parametrizado en las propiedades Iterator y NodeType del contenedor.

Variables insertadas para la biblioteca estándar

Para P0607R0, hay algunas variables habituales declaradas en la biblioteca estándar que ahora se declaran en línea.

Características de anexo D en desuso

El anexo D de la versión estándar de C++ contiene todas las características que están en desuso, como shared_ptr::unique(), <codecvt> y namespace std::tr1. Cuando se establece la opción del compilador /std:c++17 o posterior, casi todas las características de la biblioteca estándar que hay en el anexo D se marcan como en desuso. Para más información, vea el artículo Las características de la biblioteca estándar que hay en el anexo D se han marcado como en desuso.

El espacio de nombres std::tr2::sys de <experimental/filesystem> ahora emite una advertencia de desuso en /std:c++14 de forma predeterminada, y ahora se ha eliminado en /std:c++17 y la versión posterior de forma predeterminada.

Se ha mejorado la conformidad en <iostream> evitando una extensión no estándar (especializaciones explícitas en clase).

La biblioteca estándar ahora usa plantillas de variables de forma interna.

La biblioteca estándar se actualizó en respuesta a los cambios del compilador de C++17. Las actualizaciones incluyen la adición de noexcept en el sistema de tipos y la eliminación de especificaciones de excepciones dinámicas.

Cambio de orden parcial

El compilador ahora rechaza correctamente el código siguiente y muestra el mensaje de error correcto:

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(const T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);    // C2668
}
t161.cpp
t161.cpp(16): error C2668: 'f': ambiguous call to overloaded function
t161.cpp(8): note: could be 'int f<int*>(const T &)'
        with
        [
            T=int*
        ]
t161.cpp(2): note: or       'int f<int>(int*)'
t161.cpp(16): note: while trying to match the argument list '(int*)'

El problema del ejemplo anterior es que hay dos diferencias en los tipos (const frente a non-const y pack frente a non-pack). Quite una de las diferencias para eliminar el error del compilador. Después, el compilador puede ordenar inequívocamente las funciones.

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);
}

Controladores de excepciones

Los controladores de las referencias a un tipo de matriz o función no coinciden nunca con ningún objeto de excepción. Ahora, el compilador respeta esta regla y emite una advertencia de nivel 4, C4843. Además, ya no coincide con un controlador de char* o wchar_t* en un literal de cadena cuando se usa /Zc:strictStrings .

int main()
{
    try {
        throw "";
    }
    catch (int (&)[1]) {} // C4843 (This should always be dead code.)
    catch (void (&)()) {} // C4843 (This should always be dead code.)
    catch (char*) {} // This should not be a match under /Zc:strictStrings
}
warning C4843: 'int (&)[1]': An exception handler of reference to array or function type is unreachable, use 'int*' instead
warning C4843: 'void (__cdecl &)(void)': An exception handler of reference to array or function type is unreachable, use 'void (__cdecl*)(void)' instead

Este código evita el error:

catch (int (*)[1]) {}

El espacio de nombres std::tr1 esta en desuso

El espacio de nombres std::tr1 no estándar se ha marcado como en desuso en los modos de C++14 y C++17. En la versión 15.5 de Visual Studio 2017, el código siguiente genera el error C4996:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::tr1::function<int (int, int)> f = std::plus<int>(); //C4996
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}
warning C4996: 'std::tr1': warning STL4002: The non-standard std::tr1 namespace and TR1-only machinery are deprecated and will be REMOVED. You can define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING to acknowledge that you have received this warning.

Para corregirlo, quite la referencia al espacio de nombres tr1:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::function<int (int, int)> f = std::plus<int>();
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}

Las características de la biblioteca estándar que hay en el anexo D se han marcado como en desuso

Cuando se establece el modo /std:c++17 o el modificador del compilador posterior, casi todas las características de la biblioteca estándar que hay en el anexo D se marcan como en desuso.

En la versión 15.5 de Visual Studio 2017, el código siguiente genera el error C4996:

#include <iterator>

class MyIter : public std::iterator<std::random_access_iterator_tag, int> {
public:
    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");
warning C4996: 'std::iterator<std::random_access_iterator_tag,int,ptrdiff_t,_Ty*,_Ty &>::pointer': warning STL4015: The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. (The <iterator> header is NOT deprecated.) The C++ standard has never required user-defined iterators to derive from std::iterator. To fix this warning, stop deriving from std::iterator and start providing publicly accessible typedefs named iterator_category, value_type, difference_type, pointer, and reference. Note that value_type is required to be non-const, even for constant iterators. You can define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning.

Para corregirlo, siga las instrucciones que hay en el texto de advertencia, tal como se indica en el código siguiente:

#include <iterator>

class MyIter {
public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef int value_type;
    typedef ptrdiff_t difference_type;
    typedef int* pointer;
    typedef int& reference;

    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");

Variables locales sin referencias

En la versión 15.5 de Visual Studio, se emite una advertencia C4189 en más casos, tal como se indica en el código siguiente:

void f() {
    char s[2] = {0}; // C4189. Either use the variable or remove it.
}
warning C4189: 's': local variable is initialized but not referenced

Para corregirlo, quite la variable que no se use.

Comentarios en una sola línea

En la versión 15.5 de Visual Studio 2017, el compilador de C ya no emite las advertencias C4001 y C4179. Anteriormente, solo se emitían en el conmutador de compilador de /Za . Las advertencias ya no son necesarias porque los comentarios de una sola línea han formado parte del estándar C desde C99.

/* C only */
#pragma warning(disable:4001) // C4619
#pragma warning(disable:4179)
// single line comment
//* single line comment */
warning C4619: #pragma warning: there is no warning number '4001'

Si ya no es necesario que el código sea compatible con versiones anteriores, puede quitar la supresión de C4001 y C4179 para evitar la advertencia. De lo contrario, suprima solo C4619.

/* C only */

#pragma warning(disable:4619)
#pragma warning(disable:4001)
#pragma warning(disable:4179)

// single line comment
/* single line comment */

Atributos __declspec con vinculación extern "C"

En versiones anteriores de Visual Studio, el compilador ignoraba los atributos __declspec(...) cuando __declspec(...) se aplicaba antes de la especificación de vinculación extern "C". Este comportamiento provocaba que se generase código que los usuarios no tenían intención de generar, con posibles implicaciones en el tiempo de ejecución. La advertencia C4768 se agregó en la versión 15.3 de Visual Studio, pero estaba desactivada de forma predeterminada. En la versión 15.5 de Visual Studio 2017, la advertencia está habilitada de forma predeterminada.

__declspec(noinline) extern "C" HRESULT __stdcall // C4768
warning C4768: __declspec attributes before linkage specification are ignored

Para corregir el error, coloque la especificación de vinculación antes del atributo __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

Esta nueva advertencia C4768 se muestra en algunos encabezados de Windows SDK que se enviaron con la versión 15.3 de Visual Studio 2017 o una posterior (por ejemplo, la versión 10.0.15063.0, también conocida como RS2 SDK). Sin embargo, las versiones posteriores de los encabezados de Windows SDK (concretamente, ShlObj.h y ShlObj_core.h) se han corregido para que no generen la advertencia. Cuando se le muestre esta advertencia en los encabezados de Windows SDK, puede hacer lo siguiente:

  1. Cambie al Windows SDK más reciente incluido en la versión 15.5 de Visual Studio 2017.

  2. Desactive la advertencia de #include de la instrucción del encabezado de Windows SDK:

   #pragma warning (push)
   #pragma warning(disable:4768)
   #include <shlobj.h>
   #pragma warning (pop)

Vinculación de extern constexpr

En versiones anteriores de Visual Studio, el compilador siempre proporcionaba una vinculación interna de variable de constexpr aunque la variable se marcase como extern. En la versión 15.5 de Visual Studio 2017, un nuevo conmutador de compilador ( /Zc:externConstexpr ) permite un comportamiento correcto que cumple con los estándares. Este comportamiento acabará convirtiéndose en el conmutador predeterminado.

extern constexpr int x = 10;
error LNK2005: "int const x" already defined

Si un archivo de encabezado contiene una variable declarada como extern constexpr, tiene que marcarse como __declspec(selectany) para que tengan sus declaraciones duplicadas combinadas correctamente:

extern constexpr __declspec(selectany) int x = 10;

No se puede usar typeid en tipos de clase incompleta

En versiones anteriores de Visual Studio, el compilador permitía de forma incorrecta el código siguiente, lo que podía generar información de tipo incorrecto. En la versión 15.5 de Visual Studio 2017, el compilador genera correctamente un error:

#include <typeinfo>

struct S;

void f() { typeid(S); } //C2027 in 15.5
error C2027: use of undefined type 'S'

Tipo de destino std::is_convertible

std::is_convertible requiere que el tipo de destino sea un tipo de valor devuelto válido. En versiones anteriores de Visual Studio, el compilador permitía incorrectamente los tipos abstractos, lo que podía provocar una resolución de sobrecarga incorrecta y un comportamiento no intencionado del tiempo de ejecución. El código siguiente ahora genera correctamente el error C2338:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D, B>::value, "fail"); // C2338 in 15.5

Para evitarlo, al usar is_convertible debe comparar los tipos de puntero, ya que una comparación de un tipo que no sea de puntero podría provocar errores si uno de los tipos es abstracto:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D *, B *>::value, "fail");

Eliminación de especificaciones de excepción dinámica y noexcept

En C++17, throw() es un alias de noexcept, throw(<type list>) y throw(...) se quitan, y determinados tipos pueden incluir noexcept. Este cambio puede generar problemas de compatibilidad de código fuente con código que sea conforme a C++14 o versiones anteriores. El conmutador /Zc:noexceptTypes- puede usarse para volver a la versión C++14 de noexcept , pero se seguirá usando el modo C++17 a nivel general. Esto le permite actualizar el código fuente para que sea conforme con C++17 sin tener que reescribir todo el código de throw() a la vez.

Ahora, el compilador también diagnostica más especificaciones de excepción no coincidentes en las declaraciones del modo C++17 o con la opción /permissive- mediante la nueva advertencia C5043.

El código siguiente genera los errores C5043 y C5040 en la versión 15.5 de Visual Studio 2017 cuando se aplica el conmutador /std:c++17 :

void f() throw(); // equivalent to void f() noexcept;
void f() {} // warning C5043
void g() throw(); // warning C5040

struct A {
    virtual void f() throw();
};

struct B : A {
    virtual void f() { } // error C2694
};

Para quitar los errores mientras sigue usando /std:c++17 , agregue el conmutador /Zc:noexceptTypes- a la línea de comandos o bien actualice el código para que use noexcept , tal como se indica en el ejemplo siguiente:

void f() noexcept;
void f() noexcept { }
void g() noexcept(false);

struct A {
    virtual void f() noexcept;
};

struct B : A {
    virtual void f() noexcept { }
};

Variables alineadas

Ahora, los miembros de datos constexpr estáticos son inline de forma implícita, lo que significa que su declaración en una clase es ahora su definición. Usar definiciones fuera de línea para un miembro de datos static constexpr es redundante y está en desuso. En la versión 15.5 de Visual Studio 2017, al aplicar el modificador /std:c++17 , el código siguiente ahora genera la advertencia C5041:

struct X {
    static constexpr int size = 3;
};
const int X::size; // C5041: 'size': out-of-line definition for constexpr static data member is not needed and is deprecated in C++17

La advertencia C4768 extern "C" __declspec(...) ahora está activada de forma predeterminada

La advertencia se agregó en la versión 15.3 de Visual Studio 2017, pero estaba desactivada de forma predeterminada. En la versión 15.5 de Visual Studio 2017, la advertencia está activada de forma predeterminada. Para obtener más información, consulte Nueva advertencia en los atributos __declspec.

Funciones establecidas como valor predeterminado y __declspec(nothrow)

Anteriormente, el compilador permitía que las funciones predeterminadas se declarasen con __declspec(nothrow) cuando las funciones de base/miembro correspondientes permitían excepciones. Este comportamiento es contrario al estándar de C++ y puede provocar un comportamiento indefinido en el entorno de ejecución. El estándar requiere que estas funciones se definan como eliminadas si se produce un error de coincidencia en la especificación de excepciones. En /std:c++17 , el código siguiente genera la advertencia C2280:

struct A {
    A& operator=(const A& other) { // No exception specification; this function may throw.
        ...
    }
};

struct B : public A {
    __declspec(nothrow) B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1; // error C2280: attempting to reference a deleted function.
             // Function was implicitly deleted because the explicit exception
             // specification is incompatible with that of the implicit declaration.
}

Para corregir este código, elimine __declspec(nothrow) de la función predeterminada, o bien elimine = default y proporcione una definición para la función junto con el control de excepciones que sea necesario:

struct A {
    A& operator=(const A& other) {
        // ...
    }
};

struct B : public A {
    B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1;
}

noexcept y especializaciones parciales

Con noexcept en el sistema de tipo, es posible que las especializaciones parciales para hacer coincidir tipos "callable" determinados no se puedan compilar o que no se pueda elegir la plantilla principal debido a que falte una especialización parcial para los punteros de función noexcept.

En tales casos, es posible que tenga que agregar más especializaciones parciales para controlar los punteros de función noexcept y los punteros noexcept en las funciones de miembro. Estas sobrecargas solo son lícitas en el modo /std:c++17 o posterior. Si hay que conservar la compatibilidad de C++14 con versiones anteriores y está escribiendo código que utilizan otros usuarios, debe restringir estas nuevas sobrecargas a las directivas de #ifdef. Si está trabajando en un módulo autocontenido, en lugar de usar restricciones de #ifdef, puede compilar con el conmutador /Zc:noexceptTypes- .

El siguiente código se compila en /std:c++14 , pero no en /std:c++17 , con el error C2027:

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // C2027: use of undefined type 'A<T>'
}

El código siguiente se procesa correctamente en /std:c++17 porque el compilador elige la nueva especialización parcial A<void (*)() noexcept>:

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <>
struct A<void(*)() noexcept>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // OK
}

Mejoras de conformidad en la versión 15.6

Elementos fundamentales de biblioteca para C++17 V1

P0220R1 incorpora especificaciones técnicas de elementos fundamentales de biblioteca para C++17 en el estándar. Trata las actualizaciones de <experimental/tuple>, <experimental/optional>, <experimental/functional>, <experimental/any>, <experimental/string_view>, <experimental/memory>, <experimental/memory_resource> y <experimental/algorithm>.

C++17: Mejora de la deducción de argumentos de plantilla de clase para la biblioteca estándar

P0739R0 desplaza adopt_lock_t al frente de la lista de parámetros para que scoped_lock pueda permitir un uso coherente de scoped_lock. También se permite que el constructor std::variant participe en la resolución de sobrecarga en más casos para habilitar la asignación de copias.

Mejoras de conformidad en la versión 15.7

C++17: Nueva redacción de constructores de herencia

P0136R1 especifica que una declaración using que designa a un constructor ahora hace que los constructores de clase base correspondientes sean visibles para las inicializaciones de la clase derivada, en lugar de declarar más constructores de esta. Esta nueva redacción es un cambio con respecto a C ++ 14. En la versión 15.7 de Visual Studio 2017 y en las posteriores, en el modo /std:c++17 y posterior, el código válido en C++14 y que usa constructores de herencia puede no ser válido o es posible que tenga otras semánticas.

En el ejemplo siguiente se muestra el comportamiento de C++14:

struct A {
    template<typename T>
    A(T, typename T::type = 0);
    A(int);
};

struct B : A {
    using A::A;
    B(int n) = delete; // Error C2280
};

B b(42L); // Calls B<long>(long), which calls A(int)
          //  due to substitution failure in A<long>(long).

En el ejemplo siguiente se muestra el comportamiento de /std:c++17 en Visual Studio 15.7:

struct A {
    template<typename T>
    A(T, typename T::type = 0);
    A(int);
};

struct B : A {
    using A::A;
    B(int n)
    {
        //do something
    }
};

B b(42L); // now calls B(int)

Para obtener más información, vea Constructores.

C++17: Inicialización de agregados extendidos

P0017R1

Si el constructor de una clase base no es público, pero es accesible para una clase derivada, en el modo /std:c++17 y posterior de la versión 15.7 de Visual Studio 2017 ya no se pueden usar llaves vacías para inicializar un objeto del tipo derivado. En el ejemplo siguiente se muestra el comportamiento correspondiente de C++14:

struct Derived;
struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // OK in C++14: Calls Derived::Derived()
               // which can call Base ctor.

En C ++ 17, Derived ahora se considera un tipo de agregado. Eso significa que la inicialización de Base mediante el constructor privado predeterminado se produce directamente como parte de la regla de inicialización de agregados extendida. Anteriormente, el constructor privado Base se llamaba a través del constructor Derived y se realizaba correctamente debido a la declaración friend. En el ejemplo siguiente se muestra el comportamiento de C++17 en la versión 15.7 de Visual Studio en el modo /std:c++17 :

struct Derived;
struct Base {
    friend struct Derived;
private:
    Base() {}
};
struct Derived : Base {
    Derived() {} // add user-defined constructor
                 // to call with {} initialization
};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // error C2248: 'Base::Base': cannot access
               // private member declared in class 'Base'

C++17: Declaración de parámetros de plantilla sin tipo con auto

P0127R2

En el modo /std:c++17 , el compilador ahora puede deducir el tipo de un argumento de plantilla que no sea del tipo que se declara con auto :

template <auto x> constexpr auto constant = x;

auto v1 = constant<5>;      // v1 == 5, decltype(v1) is int
auto v2 = constant<true>;   // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>;    // v3 == 'a', decltype(v3) is char

Una de las consecuencias de esta nueva característica es que el código válido para C++14 puede no serlo o tener otra semántica. Por ejemplo, algunas sobrecargas que no eran válidas anteriormente ahora lo son. En el ejemplo siguiente se muestra código de C++14 que se compila porque la llamada a example(p) está enlazada a example(void*);. En la versión 15.7 de Visual Studio 2017, en el modo /std:c++17 , la plantilla de función example es la mejor coincidencia.

template <int N> struct A;
template <typename T, T N> int example(A<N>*) = delete;

void example(void *);

void sample(A<0> *p)
{
    example(p); // OK in C++14
}

En el ejemplo siguiente se muestra código de C++17 en Visual Studio 15.7 en el modo /std:c++17 :

template <int N> struct A;
template <typename T, T N> int example(A<N>*);

void example(void *);

void sample(A<0> *p)
{
    example(p); // C2280: 'int example<int,0>(A<0>*)': attempting to reference a deleted function
}

C++17: Conversiones de cadena elementales (parcial)

P0067R5 incluye funciones de bajo nivel e independientes de la configuración regional para las conversiones entre cadenas y enteros, así como entre cadenas y números de punto flotante.

C++20: Evitación de "decay" no necesarios para (parcial)

P0777R1 agrega diferenciación entre el concepto de "decay" y el de la eliminación de los calificadores const o reference. El nuevo rasgo de tipo remove_reference_t reemplaza decay_t en algunos contextos. La compatibilidad con remove_cvref_t se implementa en Visual Studio 2019.

C++17: Algoritmos paralelos

En P0024R2, el TS de paralelismo se incorpora en el estándar con modificaciones menores.

C++17: hypot(x, y, z)

P0030R1 agrega tres nuevas sobrecargas a std::hypot, para los tipos float , double y long double , cada uno de los cuales tiene tres parámetros de entrada.

C++17: <filesystem>

P0218R1 adopta el TS del sistema de archivos en el estándar con algunas modificaciones de redacción.

C++17: Funciones matemáticas especiales

P0226R1 adopta especificaciones técnicas anteriores para funciones matemáticas especiales en el encabezado <cmath> estándar.

C++17: Guías de deducción para la biblioteca estándar

P0433R2 se actualiza a STL para aprovechar las ventajas de la adopción de C++17 de P0091R3, que agrega compatibilidad con la deducción de argumentos de plantilla de clase.

C++17: Reparación de conversiones de cadenas elementales

P0682R1 desplaza las nuevas funciones de conversión de cadenas elementales de P0067R5 a un nuevo encabezado <charconv>. También presenta otras mejoras, incluido el cambio de control de errores para usar std::errc en lugar de std::error_code.

C ++ 17: constexpr para char_traits (parcial)

P0426R1 incluye cambios relativos a las funciones length, compare y find del miembro std::traits_type para que se pueda usar std::string_view en expresiones constantes. (En la versión 15.6 de Visual Studio 2017, solo es compatible con Clang/LLVM. En la versión 15.7, la compatibilidad con ClXX es casi también completa).

C++17: Argumento predeterminado en la plantilla de clase principal

Este cambio de comportamiento es una condición previa para P0091R3 - Template argument deduction for class templates.

Anteriormente, el compilador omitía el argumento predeterminado en la plantilla de clase principal:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int = 0) {} // Re-definition necessary

En el modo /std:c++17 de la versión 15.7 de Visual Studio 2017 no se ignora el argumento predeterminado:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int) {} // Default argument is used

Resolución de nombres dependientes

Este cambio de comportamiento es una condición previa para P0091R3 - Template argument deduction for class templates.

En el ejemplo siguiente, el compilador de Visual Studio 15.6 y versiones anteriores resuelve D::type como B<T>::type en la plantilla de clase principal.

template<typename T>
struct B {
    using type = T;
};

template<typename T>
struct D : B<T*> {
    using type = B<T*>::type;
};

La versión 15.7 de Visual Studio 2017, en modo /std:c++17 , requiere la palabra clave typename en la instrucción using de D. Sin typename , el compilador producirá la advertencia C4346: 'B<T*>::type': dependent name is not a type y el error C2061: syntax error: identifier 'type':

template<typename T>
struct B {
    using type = T;
};

template<typename T>
struct D : B<T*> {
    using type = typename B<T*>::type;
};

C++17: Atributo [[nodiscard]]: aumento del nivel de advertencia

En la versión 15.7 de Visual Studio 2017, en el modo /std:c++17, el nivel de advertencia de C4834 se incrementa de W3 a W1. Puede deshabilitar la advertencia con una conversión a void o pasando /wd:4834 al compilador.

[[nodiscard]] int f() { return 0; }

int main() {
    f(); // warning C4834: discarding return value
         // of function with 'nodiscard'
}

Lista de inicialización de la clase base de constructor de plantilla variádica

En ediciones anteriores de Visual Studio, se permitía erróneamente sin ningún error una lista de inicialización de la clase base de constructor de plantilla variádica en la que faltaban los argumentos de plantilla. En Visual Studio 2017 versión 15.7, se genera un error del compilador.

En siguiente ejemplo de código en la versión 15.7 de Visual Studio 2017 produce el error C2614:

template<typename T>
struct B {};

template<typename T>
struct D : B<T>
{

    template<typename ...C>
    D() : B() {} // C2614: D<int>: illegal member initialization: 'B' is not a base or member
};

D<int> d;

Para corregir el error, cambie la expresión B() a B<T>().

Inicialización de agregado constexpr

Las versiones anteriores del compilador de C++ administraban incorrectamente la inicialización de agregado de constexpr . El compilador aceptaba código no válido en el que la lista de inicialización de agregados tuviera demasiados elementos y generaba un código de objeto erróneo para este. El siguiente código es un ejemplo de esto:

#include <array>
struct X {
    unsigned short a;
    unsigned char b;
};

int main() {
    constexpr std::array<X, 2> xs = { // C2078: too many initializers
        { 1, 2 },
        { 3, 4 }
    };
    return 0;
}

En la versión 15.7 de Visual Studio 2017, actualización 3 y posteriores, el ejemplo anterior ahora genera la advertencia C2078. En el siguiente ejemplo se muestra cómo reparar el código. Al inicializar un std::array con listas de inicialización de llaves anidadas, asigne a la matriz interna una lista de llaves propia:

#include <array>
struct X {
    unsigned short a;
    unsigned char b;
};

int main() {
    constexpr std::array<X, 2> xs = {{ // note double braces
        { 1, 2 },
        { 3, 4 }
    }}; // note double braces
    return 0;
}

Mejoras de conformidad en la versión 15.8

typename en identificadores incompletos

En el modo /permissive-, el compilador ya no acepta las palabras clave typename falsas en los identificadores incompletos de las definiciones de plantilla de alias. El código siguiente ahora genera la advertencia C7511:

template <typename T>
using  X = typename T; // C7511: 'T': 'typename' keyword must be 
                       // followed by a qualified name

Para corregir el error, cambie la segunda línea por using X = T;.

__declspec() en el lado derecho de las definiciones de plantilla de alias

__declspec ya no se permite en el lado derecho de una definición de plantilla de alias. Anteriormente, el compilador lo aceptaba pero omitió este código. Nunca produciría una advertencia de desuso cuando se usaba el alias.

En su lugar, se puede usar el atributo estándar de C++ [[deprecated]], que se respeta a partir de la versión 15.6 de Visual Studio 2017. El código siguiente ahora genera la advertencia C2760:

template <typename T>
using X = __declspec(deprecated("msg")) T; // C2760: syntax error:
                                           // unexpected token '__declspec',
                                           // expected 'type specifier'`

Para corregir el error, cambie el código por lo siguiente (con el atributo colocado antes del signo "=" de la definición de alias):

template <typename T>
using  X [[deprecated("msg")]] = T;

Diagnóstico de búsqueda de nombres en dos fases

La búsqueda de nombres en dos fases requiere que los nombres no dependientes que se usan en los cuerpos de las plantillas sean visibles para la plantilla en el momento de la definición. Antes, el compilador de Microsoft C++ podía dejar un nombre no encontrado como no buscado hasta el momento de crear las instancias. Ahora, por el contrario, requiere que los nombres no dependientes estén enlazados en el cuerpo de la plantilla.

Una forma de manifestarse es con una búsqueda en las clases base dependientes. Anteriormente, el compilador permitía el uso de nombres que se definían en clases base dependientes. Esto se debe a que se buscaban en el momento de la creación de instancias cuando se resolvían todos los tipos. Ahora, ese código se trata como un error. En estos casos, puede forzar la búsqueda de la variable en el momento de crear las instancias. Para ello, puede certificarla con el tipo de clase base o convertirla en dependiente (por ejemplo, agregando un puntero this->).

En el modo /permissive-, el código siguiente ahora genera la advertencia C3861:

template <class T>
struct Base {
    int base_value = 42;
};

template <class T>
struct S : Base<T> {
    int f() {
        return base_value; // C3861: 'base_value': identifier not found
    }
};

Para corregir el error, cambie la instrucción return por return this->base_value;.

Nota:

En las versiones de la biblioteca Boost.Python anteriores a la 1.70, hay una solución alternativa específica para MSVC en relación con la declaración de reenvío de una plantilla en unwind_type.hpp. En el modo /permissive-, a partir de la versión 15.8 de Visual Studio 2017 (_MSC_VER==1915), el compilador de MSVC realiza correctamente la búsqueda de nombres dependientes de argumentos (ADL). Ahora es coherente con otros compiladores, por lo que esta protección de la solución alternativa no es necesaria. Para evitar el error C3861: 'unwind_type': identifier not found, actualice la biblioteca Boost.Python.

Declaraciones y definiciones de reenvío en el espacio de nombres std

El estándar de C++ no permite a los usuarios agregar declaraciones o definiciones de reenvío en el espacio de nombres std. Ahora, si se agregan declaraciones o definiciones al espacio de nombres std o a un espacio de nombres situado dentro del espacio de nombres std, se produce un comportamiento indefinido.

Microsoft tiene previsto trasladar en algún momento la ubicación donde se definen ciertos tipos de biblioteca estándar. Este cambio interrumpirá el código existente que agrega declaraciones de reenvío al espacio de nombres std. Una nueva advertencia, C4643, permite identificar estos problemas de origen. La advertencia se habilita en el modo /default y está desactivada de forma predeterminada. Afectará a los programas que se compilen con /Wall o /WX .

El código siguiente ahora produce C4643:

namespace std {
    template<typename T> class vector;  // C4643: Forward declaring 'vector'
                                        // in namespace std is not permitted
                                        // by the C++ Standard`
}

Para corregir el error, use una directiva #include , en vez de una declaración de reenvío:

#include <vector>

Constructores que se delegan a sí mismos

El estándar de C++ sugiere que un compilador debería emitir un diagnóstico cuando un constructor de delegación se delegue a sí mismo. El compilador de Microsoft C++ en los modos /std:c++17 y /std:c++latest ahora genera la advertencia C7535.

Sin este error, el programa siguiente se compilará, pero generará un bucle infinito:

class X {
public:
    X(int, int);
    X(int v) : X(v){} // C7535: 'X::X': delegating constructor calls itself
};

Para evitar el bucle infinito, deléguelo a otro constructor:

class X {
public:

    X(int, int);
    X(int v) : X(v, 0) {}
};

offsetof con expresiones constantes

offsetof tradicionalmente se ha implementado con una macro que requiere reinterpret_cast. Este uso no es válido en los contextos que requieran una expresión constante, pero el compilador de Microsoft C++ tradicionalmente lo ha permitido. La macro offsetof que se distribuye como parte de la biblioteca estándar usa correctamente una función intrínseca del compilador ( __builtin_offsetof ), pero muchas personas han empleado el truco de la macro para definir otra macro offsetof propia.

En la versión 15.8 de Visual Studio 2017, el compilador restringe las áreas en las que pueden aparecer estos operadores reinterpret_cast en el modo predeterminado para ayudar a que el código pueda ajustarse al comportamiento estándar de C++. En /permissive-, las restricciones son inclusos más estrictas. El uso del resultado de offsetof en lugares que requieren expresiones constantes puede producir código que emite la advertencia C4644 o C2975.

El código siguiente genera la advertencia C4644 en el modo predeterminado y en los modos /std:c++17 , y la advertencia C2975 en el modo /permissive-:

struct Data {
    int x;
};

// Common pattern of user-defined offsetof
#define MY_OFFSET(T, m) (unsigned long long)(&(((T*)nullptr)->m))

int main()

{
    switch (0) {
    case MY_OFFSET(Data, x): return 0; // C4644: usage of the
        // macro-based offsetof pattern in constant expressions
        // is non-standard; use offsetof defined in the C++
        // standard library instead
        // OR
        // C2975: invalid template argument, expected
        // compile-time constant expression

    default: return 1;
    }
}

Para corregir el error, use offsetof tal y como se define mediante <cstddef>:

#include <cstddef>

struct Data {
    int x;
};

int main()
{
    switch (0) {
    case offsetof(Data, x): return 0;
    default: return 1;
    }
}

Calificadores cv en las clases base sujetas a la expansión de paquete

Las versiones anteriores del compilador de Microsoft C++ no detectaban que una clase base tenía calificadores cv si esta también estaba sujeta a una expansión de paquete.

En la versión 15.8 de Visual Studio 2017, en el modo /permissive-, el código siguiente genera la advertencia C3770:

template<typename... T>
class X : public T... { };

class S { };

int main()
{
    X<const S> x; // C3770: 'const S': is not a valid base class
}

Palabra clave template y nested-name-specifiers

En el modo /permissive-, el compilador ahora requiere que la palabra clave template preceda a un template-name si va después de un nested-name-specifier dependiente.

El código siguiente en el modo /permissive- ahora genera el error C7510:

template<typename T> struct Base
{
    template<class U> void example() {}
};

template<typename T>
struct X : Base<T>
{
    void example()
    {
        Base<T>::example<int>(); // C7510: 'example': use of dependent
            // template name must be prefixed with 'template'
            // note: see reference to class template instantiation
            // 'X<T>' being compiled
    }
};

Para corregir el error, agregue la palabra clave template a la instrucción Base<T>::example<int>();, como se muestra en el ejemplo siguiente:

template<typename T> struct Base
{
    template<class U> void example() {}
};

template<typename T>
struct X : Base<T>
{
    void example()
    {
        // Add template keyword here:
        Base<T>::template example<int>();
    }
};

Mejoras de conformidad en la versión 15.9

Orden de evaluación de izquierda a derecha para los operadores ->*, [], >> y <<

A partir de C++17, los operandos de los operadores ->*, [], >> y << deben evaluarse en orden de izquierda a derecha. Hay dos casos en los que el compilador no puede garantizar este orden:

  • cuando una de las expresiones del operando es un objeto que se pasa por valor o contiene un objeto que se pasa por valor, o

  • cuando se compila con /clr y uno de los operandos es un campo de un objeto o un elemento de una matriz.

El compilador emite una advertencia C4866 cuando no puede garantizar la evaluación de izquierda a derecha. El compilador genera esta advertencia solo si se especifica /std:c++17 o posterior, ya que el requisito de orden de izquierda a derecha de estos operadores se introdujo en C++17.

Para resolver esta advertencia, primero debe considerar si es necesario la evaluación de izquierda a derecha de los operandos. Por ejemplo, podría ser necesario cuando la evaluación de los operandos podría producir efectos secundarios dependientes del orden. El orden en que se evalúan los operandos no tiene un efecto observable en muchos casos. Si el orden de evaluación debe ser de izquierda a derecha, considere si puede pasar los operandos por referencia const en su lugar. Este cambio elimina la advertencia en el siguiente ejemplo de código:

// C4866.cpp
// compile with: /w14866 /std:c++17

class HasCopyConstructor
{
public:
    int x;

    HasCopyConstructor(int x) : x(x) {}
    HasCopyConstructor(const HasCopyConstructor& h) : x(h.x) { }
};

int operator>>(HasCopyConstructor a, HasCopyConstructor b) { return a.x >> b.x; }

// This version of operator>> does not trigger the warning:
// int operator>>(const HasCopyConstructor& a, const HasCopyConstructor& b) { return a.x >> b.x; }

int main()
{
    HasCopyConstructor a{ 1 };
    HasCopyConstructor b{ 2 };

    a>>b;        // C4866 for call to operator>>
};

Identificadores en las plantillas de alias de miembro

Un identificador utilizado en una definición de plantilla de alias de miembro debe declararse antes de su uso.

En versiones anteriores del compilador, se permitía el código siguiente. En la versión 15.9 de Visual Studio 2017, en el modo /permissive-, el compilador genera la advertencia C3861:

template <typename... Ts>
struct A
{
  public:
    template <typename U>
    using from_template_t = decltype(from_template(A<U>{})); // C3861:
        // 'from_template': identifier not found

  private:
    template <template <typename...> typename Type, typename... Args>
    static constexpr A<Args...> from_template(A<Type<Args...>>);
};

A<>::from_template_t<A<int>> a;

Para corregir el error, declare from_template antes de from_template_t.

Cambios de módulos

En Visual Studio 2017, versión 15.9, el compilador genera la advertencia C5050 cuando las opciones de la línea de comandos para los módulos no son coherentes entre el lado de creación del módulo y el de consumo del módulo. En el ejemplo siguiente, hay dos problemas:

  • En el consumo (main.cpp), la opción /EHsc no se ha especificado.

  • La versión de C++ es /std:c++17 en la creación y /std:c++14 en el consumo.

cl /EHsc /std:c++17 m.ixx /experimental:module
cl /experimental:module /module:reference m.ifc main.cpp /std:c++14

Para ambos casos, el compilador genera la advertencia C5050:

warning C5050: Possible incompatible environment while
importing module 'm': mismatched C++ versions.
Current "201402" module version "201703".

El compilador también genera la advertencia C7536 cada vez que se manipula el archivo .ifc. El encabezado de la interfaz del módulo contiene un hash SHA2 del contenido debajo de él. En la importación, se aplica hash al archivo .ifc y, luego, se compara con el hash proporcionado en el encabezado. Si no coinciden, se produce el error C7536:

error C7536: ifc failed integrity checks.
Expected SHA2: '66d5c8154df0c71d4cab7665bab4a125c7ce5cb9a401a4d8b461b706ddd771c6'

Ordenación parcial que incluye alias y contextos no deducidos

Las implementaciones divergen en la aplicación de las reglas de ordenación parcial que afectan a los alias en contextos no deducidos. En el ejemplo siguiente, GCC y el compilador de Microsoft C++ (en modo /permissive-) producirán un error, mientras que Clang acepta el código.

#include <utility>
using size_t = std::size_t;

template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;

template <class T, class Alloc>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
    return 1;
}

template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
    return 2;
}

struct Alloc
{
    static constexpr size_t size = 10;
};

int main()
{
    A<void> a;
    AlignedStorage<Alloc::size> buf;
    if (f<Alloc>(a, buf) != 2)
    {
        return 1;
    }

    return 0;
}

El ejemplo anterior genera la advertencia C2668:

partial_alias.cpp(32): error C2668: 'f': ambiguous call to overloaded function
partial_alias.cpp(18): note: could be 'int f<Alloc,void>(A<void> &,const AlignedBuffer<10,4> &)'
partial_alias.cpp(12): note: or       'int f<Alloc,A<void>>(Alloc &,const AlignedBuffer<10,4> &)'
        with
        [
            Alloc=A<void>
        ]
partial_alias.cpp(32): note: while trying to match the argument list '(A<void>, AlignedBuffer<10,4>)'

La divergencia de implementación se debe a una regresión en la redacción estándar de C++. La resolución al problema central 2235 quitó algún texto que permitiría ordenar estas sobrecargas. El actual estándar C++ no proporciona un mecanismo para ordenar parcialmente estas funciones, por lo que se consideran ambiguas.

Como solución alternativa, le recomendamos que no confíe en la ordenación parcial para resolver este problema. En su lugar, utilice SFINAE para quitar las sobrecargas particulares. En el ejemplo siguiente, se utiliza una clase auxiliar IsA para quitar la primera sobrecarga cuando Alloc es una especialización de A:

#include <utility>
using size_t = std::size_t;

template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;

template <typename T> struct IsA : std::false_type {};
template <typename T> struct IsA<A<T>> : std::true_type {};

template <class T, class Alloc, typename = std::enable_if_t<!IsA<Alloc>::value>>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
    return 1;
}

template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
    return 2;
}

struct Alloc
{
    static constexpr size_t size = 10;
};

int main()
{
    A<void> a;
    AlignedStorage<Alloc::size> buf;
    if (f<Alloc>(a, buf) != 2)
    {
        return 1;
    }

    return 0;
}

Expresiones no válidas y tipos no literales en las definiciones de funciones basadas en un modelo

Las expresiones no válidas y los tipos no literales ahora se diagnostican correctamente en las definiciones de funciones basadas en un modelo que son especializadas de forma explícita. Antes, no se emitían estos errores para la definición de función. Aun así, la expresión no válida o el tipo no literal se diagnosticaban igualmente si se evaluaban como parte de una expresión constante.

En versiones anteriores de Visual Studio, el código siguiente se compilaba sin advertencias:

void g();

template<typename T>
struct S
{
    constexpr void f();
};

template<>
constexpr void S<int>::f()
{
    g(); // C3615 in 15.9
}

En la versión 15.9 de Visual Studio 2017, el código siguiente genera el error C3615:

error C3615: constexpr function 'S<int>::f' cannot result in a constant expression.
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of 'g'.

Para evitar el error, quite el calificador constexpr de la creación de instancia explícita de la función f().

Vea también

Conformidad del lenguaje Microsoft C/C++