38 votes

Pourquoi l'expansion de pack de paramètres fonctionne-t-elle différemment avec différents compilateurs C ++?

Paramètre pack d'extension est renversée par la VS2015 compilateur.

J'ai le code suivant:

#include <iostream>
#include <vector>


template <typename... T>
void f_Swallow(T &&...)
{
}

template <typename... T>
std::vector<int> f(T ...arg)
{
    std::vector<int> result;
    f_Swallow
    (
        [&]()
        {

            result.push_back(arg);
            return true;
        }
        ()...
    ) ;
    return result;
}


using namespace std;
int main()
{
    auto vec = f(1,2,3,4);

    for (size_t i = 0; i < vec.size(); ++i)
        cout << vec[i] << endl;
}

Lorsque j'exécute ce code dans XCode (clang-700.1.81), j'obtiens ce résultat:

1
2
3
4

Mais le même code à exécuter dans VS2015 produit de cette sortie:

4
3
2
1

Pourquoi le paramètre packs élargi différemment selon le compilateur? Est-il un moyen de le fixer sans vérification de la plate-forme et la version de compilateur? N'a pas la garantie standard de rien à propos de l'expansion de l'ordre?

47voto

TartanLlama Points 1461

Ce n'est pas la commande de paramètre pack d'extension qui est différent, c'est l'ordre de l'argument de fonction d'évaluation.

f_Swallow
(
    [&]()
    {

        result.push_back(arg);
        return true;
    }
    ()...
) ;

Pour des raisons de concision, permet de donner lambda le nom funcNN est le numéro de paramètre. Quatre arguments, le paramètre pack sera élargi par n'importe quel compilateur conforme en cela:

f_Swallow(func1(), func2(), func3, func4()) ;

L'ordre d'évaluation des arguments d'une fonction n'est pas spécifié dans C++. Le compilateur pourrait les évaluer dans l'ordre (comme votre version de Clang), dans l'ordre inverse (comme votre version de MSVC), ou dans n'importe quel ordre il aime. Vous ne pouvez pas compter sur l'ordre d'évaluation.

Pour obtenir ce que vous voulez, vous pouvez placer les expressions dans un contexte dans lequel l'ordre d'évaluation est spécifié. Par exemple:

template <typename... T>
std::vector<int> f(T ...arg)
{
    std::vector<int> result;
    (void)std::initializer_list<int> { (result.push_back(arg), 0)... };
    return result;
}

En C++17, vous serez en mesure d'effectuer les opérations suivantes avec des fois des expressions:

template <typename... T>
std::vector<int> f(T ...arg)
{
    std::vector<int> result;
    (result.push_back(arg), ...);
    return result;
}

11voto

J'ai pensé qu'il pourrait aussi être écrit comme ceci:

 template <typename... T>
std::vector<int> f(T ...arg)
{
    std::vector<int> result{ arg... };
    return result;
}
 

Pas besoin de créer un std :: initializer_list factice

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X