Подтвердить что ты не робот

Почему расширение пакета параметров работает по-разному с разными компиляторами С++?

Расширение пакета параметров отменяется компилятором VS2015.

У меня есть следующий код:

#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;
}

Когда я запускаю этот код в XCode (clang-700.1.81), я получаю этот результат:

1
2
3
4

Но тот же самый код в VS2015 производит этот вывод:

4
3
2
1

Почему пакеты параметров расширяются по-разному в зависимости от компилятора? Есть ли способ исправить это, не проверяя версию платформы и компилятора? Не гарантирует ли стандарт что-либо о заказе расширения?

4b9b3361

Ответ 1

Это не порядок расширения пакета параметров, который отличается, это порядок оценки аргумента функции.

f_Swallow
(
    [&]()
    {

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

Для краткости давайте просто укажем лямбда имя funcN, где N - номер параметра. Учитывая четыре аргумента, пакет параметров будет расширяться любым соответствующим компилятором следующим образом:

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

Порядок оценки аргументов функции не указан в С++. Компилятор мог бы оценивать их по порядку (например, вашу версию Clang), в обратном порядке (например, вашу версию MSVC) или в любом порядке, который ему нравится. Вы не можете рассчитывать на порядок оценки.

Чтобы получить то, что вы хотите, вы можете поместить выражения в контекст, в котором указан порядок оценки. Например:

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;
}

В С++ 17 вы сможете сделать следующее с сгибами выражения:

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

Ответ 2

Я подумал, что он также может быть написан так:

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

Не нужно создавать dummy std:: initializer_list